From 5f013db69cd3a1ddafd271469c58f93128ca3e9a Mon Sep 17 00:00:00 2001 From: DanGould Date: Fri, 13 Jan 2023 13:00:26 -0500 Subject: [PATCH 01/24] Init payjoin-relay with turn_server_udp --- Cargo.lock | 342 ++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 +- payjoin-relay/Cargo.toml | 19 +++ payjoin-relay/src/main.rs | 137 +++++++++++++++ 4 files changed, 499 insertions(+), 1 deletion(-) create mode 100644 payjoin-relay/Cargo.toml create mode 100644 payjoin-relay/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 34839f74d..755822e32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,26 @@ dependencies = [ "memchr", ] +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "async-trait" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "705339e0e4a9690e2908d2b3d049d85682cf19fbd5782494498fbf7003a6a282" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "atty" version = "0.2.14" @@ -191,6 +211,15 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + [[package]] name = "bumpalo" version = "3.11.1" @@ -248,6 +277,21 @@ version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cca491388666e04d7248af3f60f0c40cfb0991c72205595d7c396e3510207d1a" +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + [[package]] name = "const_fn" version = "0.4.9" @@ -297,6 +341,21 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +[[package]] +name = "crc" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53757d12b596c16c78b83458d732a5d1a17ab3f53f2f7412f6fb57cc8a140ab3" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" + [[package]] name = "crc32fast" version = "1.3.2" @@ -306,6 +365,26 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "discard" version = "1.0.4" @@ -403,6 +482,21 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "futures" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.25" @@ -410,6 +504,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -418,12 +513,34 @@ version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" +[[package]] +name = "futures-executor" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + [[package]] name = "futures-io" version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" +[[package]] +name = "futures-macro" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "futures-sink" version = "0.3.25" @@ -442,8 +559,11 @@ version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" dependencies = [ + "futures-channel", "futures-core", "futures-io", + "futures-macro", + "futures-sink", "futures-task", "memchr", "pin-project-lite", @@ -451,6 +571,16 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.2.8" @@ -667,6 +797,16 @@ version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.17" @@ -682,12 +822,30 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" +[[package]] +name = "md-5" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" +dependencies = [ + "digest", +] + [[package]] name = "memchr" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + [[package]] name = "mime" version = "0.3.16" @@ -734,6 +892,18 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nix" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +dependencies = [ + "bitflags", + "cfg-if", + "libc", + "memoffset", +] + [[package]] name = "num_cpus" version = "1.15.0" @@ -795,6 +965,29 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys 0.42.0", +] + [[package]] name = "payjoin" version = "0.5.1-alpha" @@ -819,6 +1012,22 @@ dependencies = [ "reqwest", ] +[[package]] +name = "payjoin-relay" +version = "0.1.0" +dependencies = [ + "base64", + "bitcoincore-rpc 0.14.0", + "clap", + "env_logger", + "log", + "payjoin", + "reqwest", + "tokio", + "turn", + "webrtc-util", +] + [[package]] name = "percent-encoding" version = "2.2.0" @@ -1053,6 +1262,12 @@ dependencies = [ "windows-sys 0.36.1", ] +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + [[package]] name = "sct" version = "0.6.1" @@ -1198,6 +1413,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + [[package]] name = "slab" version = "0.4.7" @@ -1207,6 +1431,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + [[package]] name = "socket2" version = "0.4.7" @@ -1281,6 +1511,37 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "stun" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7e94b1ec00bad60e6410e058b52f1c66de3dc5fe4d62d09b3e52bb7d3b73e25" +dependencies = [ + "base64", + "crc", + "lazy_static", + "md-5", + "rand", + "ring", + "subtle", + "thiserror", + "tokio", + "url", + "webrtc-util", +] + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + [[package]] name = "syn" version = "1.0.107" @@ -1326,6 +1587,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + [[package]] name = "thiserror" version = "1.0.38" @@ -1422,11 +1692,25 @@ dependencies = [ "memchr", "mio", "num_cpus", + "parking_lot", "pin-project-lite", + "signal-hook-registry", "socket2", + "tokio-macros", "windows-sys 0.42.0", ] +[[package]] +name = "tokio-macros" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tokio-native-tls" version = "0.3.0" @@ -1483,6 +1767,31 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +[[package]] +name = "turn" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4712ee30d123ec7ae26d1e1b218395a16c87cdbaf4b3925d170d684af62ea5e8" +dependencies = [ + "async-trait", + "base64", + "futures", + "log", + "md-5", + "rand", + "ring", + "stun", + "thiserror", + "tokio", + "webrtc-util", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + [[package]] name = "unicode-bidi" version = "0.3.8" @@ -1504,6 +1813,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + [[package]] name = "untrusted" version = "0.7.1" @@ -1546,6 +1861,12 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + [[package]] name = "version_check" version = "0.9.4" @@ -1669,6 +1990,27 @@ dependencies = [ "webpki", ] +[[package]] +name = "webrtc-util" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f1db1727772c05cf7a2cfece52c3aca8045ca1e176cd517d323489aa3c6d87" +dependencies = [ + "async-trait", + "bitflags", + "bytes", + "cc", + "ipnet", + "lazy_static", + "libc", + "log", + "nix", + "rand", + "thiserror", + "tokio", + "winapi", +] + [[package]] name = "which" version = "4.3.0" diff --git a/Cargo.toml b/Cargo.toml index e42c3f24d..d8ffa521f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,2 @@ [workspace] -members = ["payjoin", "payjoin-client"] +members = ["payjoin", "payjoin-client", "payjoin-relay"] diff --git a/payjoin-relay/Cargo.toml b/payjoin-relay/Cargo.toml new file mode 100644 index 000000000..42987f992 --- /dev/null +++ b/payjoin-relay/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "payjoin-relay" +version = "0.1.0" +authors = ["Dan Gould "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +payjoin = { path = "../payjoin", features = ["sender"] } +bitcoincore-rpc = "0.14.0" +reqwest = { version = "0.11.4", features = ["blocking"] } +base64 = "0.13.0" +turn = "0.6.1" +webrtc-util = { version = "0.7.0", default-features = false, features = ["conn", "vnet"] } +clap = "2.33.3" +tokio = { version = "1.6.1", features = ["full"] } +log = "0.4.14" +env_logger = "0.9.0" \ No newline at end of file diff --git a/payjoin-relay/src/main.rs b/payjoin-relay/src/main.rs new file mode 100644 index 000000000..150bfa40d --- /dev/null +++ b/payjoin-relay/src/main.rs @@ -0,0 +1,137 @@ +use turn::auth::*; +use turn::relay::relay_static::*; +use turn::server::{config::*, *}; +use turn::Error; + +use clap::{App, AppSettings, Arg}; +use std::collections::HashMap; +use std::net::{IpAddr, SocketAddr}; +use std::str::FromStr; +use std::sync::Arc; +use tokio::net::UdpSocket; +use tokio::signal; +use tokio::time::Duration; +use webrtc_util::vnet::net::*; + +struct MyAuthHandler { + cred_map: HashMap>, +} + +impl MyAuthHandler { + fn new(cred_map: HashMap>) -> Self { + MyAuthHandler { cred_map } + } +} + +impl AuthHandler for MyAuthHandler { + fn auth_handle( + &self, + username: &str, + _realm: &str, + _src_addr: SocketAddr, + ) -> Result, Error> { + if let Some(pw) = self.cred_map.get(username) { + //log::debug!("username={}, password={:?}", username, pw); + Ok(pw.to_vec()) + } else { + Err(Error::ErrFakeErr) + } + } +} + +// RUST_LOG=trace cargo run --color=always --package turn --example turn_server_udp -- --public-ip 0.0.0.0 --users user=pass + +#[tokio::main] +async fn main() -> Result<(), Error> { + env_logger::init(); + + let mut app = App::new("TURN Server UDP") + .version("0.1.0") + .author("Rain Liu ") + .about("An example of TURN Server UDP") + .setting(AppSettings::DeriveDisplayOrder) + .setting(AppSettings::SubcommandsNegateReqs) + .arg( + Arg::with_name("FULLHELP") + .help("Prints more detailed help information") + .long("fullhelp"), + ) + .arg( + Arg::with_name("public-ip") + .required_unless("FULLHELP") + .takes_value(true) + .long("public-ip") + .help("IP Address that TURN can be contacted by."), + ) + .arg( + Arg::with_name("users") + .required_unless("FULLHELP") + .takes_value(true) + .long("users") + .help("List of username and password (e.g. \"user=pass,user=pass\")"), + ) + .arg( + Arg::with_name("realm") + .default_value("webrtc.rs") + .takes_value(true) + .long("realm") + .help("Realm (defaults to \"webrtc.rs\")"), + ) + .arg( + Arg::with_name("port") + .takes_value(true) + .default_value("3478") + .long("port") + .help("Listening port."), + ); + + let matches = app.clone().get_matches(); + + if matches.is_present("FULLHELP") { + app.print_long_help().unwrap(); + std::process::exit(0); + } + + let public_ip = matches.value_of("public-ip").unwrap(); + let port = matches.value_of("port").unwrap(); + let users = matches.value_of("users").unwrap(); + let realm = matches.value_of("realm").unwrap(); + + // Cache -users flag for easy lookup later + // If passwords are stored they should be saved to your DB hashed using turn.GenerateAuthKey + let creds: Vec<&str> = users.split(',').collect(); + let mut cred_map = HashMap::new(); + for user in creds { + let cred: Vec<&str> = user.splitn(2, '=').collect(); + let key = generate_auth_key(cred[0], realm, cred[1]); + cred_map.insert(cred[0].to_owned(), key); + } + + // Create a UDP listener to pass into pion/turn + // turn itself doesn't allocate any UDP sockets, but lets the user pass them in + // this allows us to add logging, storage or modify inbound/outbound traffic + let conn = Arc::new(UdpSocket::bind(format!("0.0.0.0:{}", port)).await?); + println!("listening {}...", conn.local_addr()?); + + let server = Server::new(ServerConfig { + conn_configs: vec![ConnConfig { + conn, + relay_addr_generator: Box::new(RelayAddressGeneratorStatic { + relay_address: IpAddr::from_str(public_ip)?, + address: "0.0.0.0".to_owned(), + net: Arc::new(Net::new(None)), + }), + }], + realm: realm.to_owned(), + auth_handler: Arc::new(MyAuthHandler::new(cred_map)), + channel_bind_timeout: Duration::from_secs(0), + }) + .await?; + + println!("Waiting for Ctrl-C..."); + signal::ctrl_c().await.expect("failed to listen for event"); + println!("\nClosing connection now..."); + server.close().await?; + + Ok(()) +} From 98a3b9436d5dd5da1c82a43f45571b20c9b20e8e Mon Sep 17 00:00:00 2001 From: DanGould Date: Fri, 13 Jan 2023 13:15:05 -0500 Subject: [PATCH 02/24] Separate send_payjoin logic --- payjoin-client/Cargo.toml | 2 +- payjoin-client/src/main.rs | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/payjoin-client/Cargo.toml b/payjoin-client/Cargo.toml index 19648cd0f..f30cecaf5 100644 --- a/payjoin-client/Cargo.toml +++ b/payjoin-client/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -payjoin = { path = "../payjoin", features = ["sender"] } +payjoin = { path = "../payjoin", features = ["sender", "receiver"] } bitcoincore-rpc = "0.14.0" reqwest = { version = "0.11.4", features = ["blocking"] } base64 = "0.13.0" diff --git a/payjoin-client/src/main.rs b/payjoin-client/src/main.rs index 4ff20cd2e..0dd886e05 100644 --- a/payjoin-client/src/main.rs +++ b/payjoin-client/src/main.rs @@ -22,6 +22,17 @@ fn main() { let bip21 = args.next().expect("Missing arguments: bip21").into_string().expect("bip21 is not UTF-8"); + let client = bitcoincore_rpc::Client::new( + &format!("http://127.0.0.1:{}", port), + bitcoincore_rpc::Auth::CookieFile(cookie_file.into()), + ) + .unwrap(); + + send_payjoin(bip21, client); + +} + +fn send_payjoin<'a>(bip21: String, client: bitcoincore_rpc::Client) -> bitcoincore_rpc::bitcoin::Txid { let link = payjoin::Uri::try_from(&*bip21).unwrap(); let link = link @@ -36,11 +47,6 @@ fn main() { let mut outputs = HashMap::with_capacity(1); outputs.insert(link.address.to_string(), amount); - let client = bitcoincore_rpc::Client::new( - &format!("http://127.0.0.1:{}", port), - bitcoincore_rpc::Auth::CookieFile(cookie_file.into()), - ) - .unwrap(); let options = bitcoincore_rpc::json::WalletCreateFundedPsbtOptions { lock_unspent: Some(true), fee_rate: Some(Amount::from_sat(2000)), @@ -76,7 +82,7 @@ fn main() { println!("Proposed psbt: {:#?}", psbt); let psbt = client.wallet_process_psbt(&serialize_psbt(&psbt), None, None, None).unwrap().psbt; let tx = client.finalize_psbt(&psbt, Some(true)).unwrap().hex.expect("incomplete psbt"); - client.send_raw_transaction(&tx).unwrap(); + client.send_raw_transaction(&tx).unwrap() } fn load_psbt_from_base64( From 1ff528f5e5945033b3e2989987f1b724c7826205 Mon Sep 17 00:00:00 2001 From: DanGould Date: Fri, 13 Jan 2023 13:27:05 -0500 Subject: [PATCH 03/24] Parse cli args using clap --- Cargo.lock | 1 + payjoin-client/Cargo.toml | 1 + payjoin-client/src/main.rs | 51 +++++++++++++++++++++++++++----------- 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 755822e32..c09b6114c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1008,6 +1008,7 @@ version = "0.1.0" dependencies = [ "base64", "bitcoincore-rpc 0.14.0", + "clap", "payjoin", "reqwest", ] diff --git a/payjoin-client/Cargo.toml b/payjoin-client/Cargo.toml index f30cecaf5..55396f5dc 100644 --- a/payjoin-client/Cargo.toml +++ b/payjoin-client/Cargo.toml @@ -11,3 +11,4 @@ payjoin = { path = "../payjoin", features = ["sender", "receiver"] } bitcoincore-rpc = "0.14.0" reqwest = { version = "0.11.4", features = ["blocking"] } base64 = "0.13.0" +clap = "2.33.3" diff --git a/payjoin-client/src/main.rs b/payjoin-client/src/main.rs index 0dd886e05..6c437434e 100644 --- a/payjoin-client/src/main.rs +++ b/payjoin-client/src/main.rs @@ -1,26 +1,49 @@ use std::collections::HashMap; use std::convert::TryFrom; + use bitcoincore_rpc::bitcoin::Amount; use bitcoincore_rpc::RpcApi; +use clap::{App, AppSettings, Arg}; use payjoin::bitcoin::util::psbt::PartiallySignedTransaction as Psbt; use payjoin::{PjUriExt, UriExt}; fn main() { - let mut args = std::env::args_os(); - let _program_name = args.next().expect("not even program name given"); - let port = args - .next() - .expect("Missing arguments: port cookie_file bip21") - .into_string() - .expect("port is not UTF-8") - .parse::() - .expect("port must be a number"); - - let cookie_file = args.next().expect("Missing arguments: cookie_file bip21"); + let mut app = App::new("payjoin-client") + .version("0.1.0") + .author("Dan Gould ") + .about("A simple payjoin client that can receive without hosting a secure endpoint using TURN") + .setting(AppSettings::DeriveDisplayOrder) + .setting(AppSettings::SubcommandsNegateReqs) + .arg(Arg::with_name("port") + .short("p") + .long("port") + .help("The bitcoind rpc port to connect to") + .takes_value(true) + .required(true)) + .arg(Arg::with_name("cookie_file") + .short("c") + .long("cookie_file") + .help("The bitcoind rpc cookie file to use for authentication") + .takes_value(true) + .required(true)) + .arg(Arg::with_name("bip21") + .short("b") + .long("bip21") + .help("The BIP21 URI to send to") + .takes_value(true) + .required(true)); + + let matches = app.clone().get_matches(); + + if matches.is_present("FULLHELP") { + app.print_long_help().unwrap(); + return; + } - let bip21 = - args.next().expect("Missing arguments: bip21").into_string().expect("bip21 is not UTF-8"); + let port = matches.value_of("port").unwrap(); + let cookie_file = matches.value_of("cookie_file").unwrap(); + let bip21 = matches.value_of("bip21").unwrap().as_ref(); let client = bitcoincore_rpc::Client::new( &format!("http://127.0.0.1:{}", port), @@ -32,7 +55,7 @@ fn main() { } -fn send_payjoin<'a>(bip21: String, client: bitcoincore_rpc::Client) -> bitcoincore_rpc::bitcoin::Txid { +fn send_payjoin<'a>(bip21: &str, client: bitcoincore_rpc::Client) -> bitcoincore_rpc::bitcoin::Txid { let link = payjoin::Uri::try_from(&*bip21).unwrap(); let link = link From 4912c6a5e57773cdc330fe3ef9012bc3e1faf1c5 Mon Sep 17 00:00:00 2001 From: DanGould Date: Fri, 13 Jan 2023 13:47:44 -0500 Subject: [PATCH 04/24] Parse arguments to receive PayJoin --- payjoin-client/src/main.rs | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/payjoin-client/src/main.rs b/payjoin-client/src/main.rs index 6c437434e..9e0e848cf 100644 --- a/payjoin-client/src/main.rs +++ b/payjoin-client/src/main.rs @@ -31,8 +31,17 @@ fn main() { .short("b") .long("bip21") .help("The BIP21 URI to send to") - .takes_value(true) - .required(true)); + .takes_value(true)) + .arg(Arg::with_name("relay") + .short("r") + .long("relay") + .help("PayJoin relay to establish p2p networking") + .takes_value(true)) + .arg(Arg::with_name("amount") + .short("a") + .long("amount") + .help("The amount to request in satoshis") + .takes_value(true)); let matches = app.clone().get_matches(); @@ -43,7 +52,6 @@ fn main() { let port = matches.value_of("port").unwrap(); let cookie_file = matches.value_of("cookie_file").unwrap(); - let bip21 = matches.value_of("bip21").unwrap().as_ref(); let client = bitcoincore_rpc::Client::new( &format!("http://127.0.0.1:{}", port), @@ -51,8 +59,13 @@ fn main() { ) .unwrap(); - send_payjoin(bip21, client); - + if matches.is_present("relay") { + println!("Configured to a receive via relay"); + std::process::exit(0); + } else { + let bip21 = matches.value_of("bip21").unwrap().as_ref(); + send_payjoin(bip21, client); + } } fn send_payjoin<'a>(bip21: &str, client: bitcoincore_rpc::Client) -> bitcoincore_rpc::bitcoin::Txid { From 59ca0a39d81c5163284d28e1c98a4b32ef1a189e Mon Sep 17 00:00:00 2001 From: DanGould Date: Fri, 13 Jan 2023 14:55:11 -0500 Subject: [PATCH 05/24] Have receiver create bip21 --- payjoin-client/src/main.rs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/payjoin-client/src/main.rs b/payjoin-client/src/main.rs index 9e0e848cf..193dee5c4 100644 --- a/payjoin-client/src/main.rs +++ b/payjoin-client/src/main.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; use std::convert::TryFrom; +use std::str::FromStr; use bitcoincore_rpc::bitcoin::Amount; @@ -60,14 +61,32 @@ fn main() { .unwrap(); if matches.is_present("relay") { - println!("Configured to a receive via relay"); - std::process::exit(0); + let relay = matches.value_of("relay").unwrap(); + let amount = matches.value_of("amount").unwrap(); + receive_payjoin(relay, amount, client); } else { let bip21 = matches.value_of("bip21").unwrap().as_ref(); send_payjoin(bip21, client); } } +fn receive_payjoin(relay: &str, amount: &str, client: bitcoincore_rpc::Client) -> bitcoincore_rpc::bitcoin::Txid { + // Ensure relay connection + + // Receiver creates the bip21 payjoin URI + let pj_receiver_address = client.get_new_address(None, None).unwrap(); + let amount = Amount::from_str(amount).unwrap(); + let pj_uri_string = format!( + "{}?amount={}&pj={}&s=secret", + pj_receiver_address.to_qr_uri(), + amount.to_btc(), + relay, + ); + let pj_uri = payjoin::Uri::from_str(&pj_uri_string).unwrap(); + let pj_uri = pj_uri.check_pj_supported().expect("Bad Uri"); + +} + fn send_payjoin<'a>(bip21: &str, client: bitcoincore_rpc::Client) -> bitcoincore_rpc::bitcoin::Txid { let link = payjoin::Uri::try_from(&*bip21).unwrap(); From 9144021009651cdd56f43057feee921108b99261 Mon Sep 17 00:00:00 2001 From: DanGould Date: Fri, 13 Jan 2023 15:26:18 -0500 Subject: [PATCH 06/24] Make TURN relay server connection --- Cargo.lock | 3 + payjoin-client/Cargo.toml | 3 + payjoin-client/src/main.rs | 150 ++++++++++++++++++++++++++++++++++--- 3 files changed, 145 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c09b6114c..a9816cb48 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1011,6 +1011,9 @@ dependencies = [ "clap", "payjoin", "reqwest", + "tokio", + "turn", + "webrtc-util", ] [[package]] diff --git a/payjoin-client/Cargo.toml b/payjoin-client/Cargo.toml index 55396f5dc..2ad0d9325 100644 --- a/payjoin-client/Cargo.toml +++ b/payjoin-client/Cargo.toml @@ -12,3 +12,6 @@ bitcoincore-rpc = "0.14.0" reqwest = { version = "0.11.4", features = ["blocking"] } base64 = "0.13.0" clap = "2.33.3" +turn = "0.6.1" +tokio = { version = "1.6.1", features = ["full"] } +webrtc-util = { version = "0.7.0", default-features = false, features = ["conn", "vnet"] } diff --git a/payjoin-client/src/main.rs b/payjoin-client/src/main.rs index 193dee5c4..483b05b54 100644 --- a/payjoin-client/src/main.rs +++ b/payjoin-client/src/main.rs @@ -1,15 +1,21 @@ use std::collections::HashMap; use std::convert::TryFrom; use std::str::FromStr; +use std::sync::Arc; - +use turn::client::*; +use turn::Error; use bitcoincore_rpc::bitcoin::Amount; use bitcoincore_rpc::RpcApi; use clap::{App, AppSettings, Arg}; use payjoin::bitcoin::util::psbt::PartiallySignedTransaction as Psbt; use payjoin::{PjUriExt, UriExt}; +use tokio::net::UdpSocket; +use tokio::time::Duration; +use webrtc_util::Conn; -fn main() { +#[tokio::main] +async fn main() -> Result<(), Error> { let mut app = App::new("payjoin-client") .version("0.1.0") .author("Dan Gould ") @@ -48,13 +54,13 @@ fn main() { if matches.is_present("FULLHELP") { app.print_long_help().unwrap(); - return; + return Ok(()); } let port = matches.value_of("port").unwrap(); let cookie_file = matches.value_of("cookie_file").unwrap(); - let client = bitcoincore_rpc::Client::new( + let bitcoind = bitcoincore_rpc::Client::new( &format!("http://127.0.0.1:{}", port), bitcoincore_rpc::Auth::CookieFile(cookie_file.into()), ) @@ -63,28 +69,150 @@ fn main() { if matches.is_present("relay") { let relay = matches.value_of("relay").unwrap(); let amount = matches.value_of("amount").unwrap(); - receive_payjoin(relay, amount, client); + // Ensure relay connection + // TURN client won't create a local listening socket by itself. + let conn = UdpSocket::bind("0.0.0.0:0").await?; + + let turn_server_addr = relay.to_string(); + + let cfg = ClientConfig { + stun_serv_addr: turn_server_addr.clone(), + turn_serv_addr: turn_server_addr, + username: "test".to_string(), + password: "test".to_string(), + realm: "test".to_string(), + software: String::new(), + rto_in_ms: 0, + conn: Arc::new(conn), + vnet: None, + }; + + let client = Client::new(cfg).await?; + + // Start listening on the conn provided. + client.listen().await?; + + // Allocate a relay socket on the TURN server. On success, it + // will return a net.PacketConn which represents the remote + // socket. + let relay_conn = client.allocate().await?; + + // The relayConn's local address is actually the transport + // address assigned on the TURN server. + println!("relayed-address={}", relay_conn.local_addr()?); + + // If you provided `-ping`, perform a ping test agaist the + let ping = true; + // relayConn we have just allocated. + if ping { + do_ping_test(&client, relay_conn).await?; + } + + receive_payjoin(relay, amount, bitcoind); + + client.close().await?; } else { let bip21 = matches.value_of("bip21").unwrap().as_ref(); - send_payjoin(bip21, client); + send_payjoin(bip21, bitcoind); } + + Ok(()) } -fn receive_payjoin(relay: &str, amount: &str, client: bitcoincore_rpc::Client) -> bitcoincore_rpc::bitcoin::Txid { - // Ensure relay connection +async fn do_ping_test( + client: &Client, + relay_conn: impl Conn + std::marker::Send + std::marker::Sync + 'static, +) -> Result<(), Error> { + // Send BindingRequest to learn our external IP + let mapped_addr = client.send_binding_request().await?; + + // Set up pinger socket (pingerConn) + //println!("bind..."); + let pinger_conn_tx = Arc::new(UdpSocket::bind("0.0.0.0:0").await?); + + // Punch a UDP hole for the relay_conn by sending a data to the mapped_addr. + // This will trigger a TURN client to generate a permission request to the + // TURN server. After this, packets from the IP address will be accepted by + // the TURN server. + //println!("relay_conn send hello to mapped_addr {}", mapped_addr); + relay_conn.send_to("Hello".as_bytes(), mapped_addr).await?; + let relay_addr = relay_conn.local_addr()?; + let pinger_conn_rx = Arc::clone(&pinger_conn_tx); + + // Start read-loop on pingerConn + tokio::spawn(async move { + let mut buf = vec![0u8; 1500]; + loop { + let (n, from) = match pinger_conn_rx.recv_from(&mut buf).await { + Ok((n, from)) => (n, from), + Err(_) => break, + }; + + let msg = match String::from_utf8(buf[..n].to_vec()) { + Ok(msg) => msg, + Err(_) => break, + }; + + println!("pingerConn read-loop: {} from {}", msg, from); + /*if sentAt, pingerErr := time.Parse(time.RFC3339Nano, msg); pingerErr == nil { + rtt := time.Since(sentAt) + log.Printf("%d bytes from from %s time=%d ms\n", n, from.String(), int(rtt.Seconds()*1000)) + }*/ + } + }); + + // Start read-loop on relay_conn + tokio::spawn(async move { + let mut buf = vec![0u8; 1500]; + loop { + let (n, from) = match relay_conn.recv_from(&mut buf).await { + Err(_) => break, + Ok((n, from)) => (n, from), + }; + + println!("relay_conn read-loop: {:?} from {}", &buf[..n], from); + + // Echo back + if relay_conn.send_to(&buf[..n], from).await.is_err() { + break; + } + } + }); + + tokio::time::sleep(Duration::from_millis(500)).await; + + /*println!( + "pinger_conn_tx send 10 packets to relay addr {}...", + relay_addr + );*/ + // Send 10 packets from relay_conn to the echo server + for _ in 0..2 { + let msg = "12345678910".to_owned(); //format!("{:?}", tokio::time::Instant::now()); + println!("sending msg={} with size={}", msg, msg.as_bytes().len()); + pinger_conn_tx.send_to(msg.as_bytes(), relay_addr).await?; + + // For simplicity, this example does not wait for the pong (reply). + // Instead, sleep 1 second. + tokio::time::sleep(Duration::from_secs(1)).await; + } + + Ok(()) +} + +fn receive_payjoin(relay: &str, amount: &str, client: bitcoincore_rpc::Client) { + // Receiver creates the bip21 payjoin URI let pj_receiver_address = client.get_new_address(None, None).unwrap(); let amount = Amount::from_str(amount).unwrap(); let pj_uri_string = format!( "{}?amount={}&pj={}&s=secret", pj_receiver_address.to_qr_uri(), - amount.to_btc(), + amount.as_btc(), relay, ); let pj_uri = payjoin::Uri::from_str(&pj_uri_string).unwrap(); - let pj_uri = pj_uri.check_pj_supported().expect("Bad Uri"); - + let _pj_uri = pj_uri.check_pj_supported().expect("Bad Uri"); } fn send_payjoin<'a>(bip21: &str, client: bitcoincore_rpc::Client) -> bitcoincore_rpc::bitcoin::Txid { From f615e5762ea9bf2c9ca5dd3fa84f7583b9636ab6 Mon Sep 17 00:00:00 2001 From: DanGould Date: Sat, 14 Jan 2023 12:27:51 -0500 Subject: [PATCH 07/24] Send Original PSBT placeholder via relay --- payjoin-client/src/main.rs | 263 ++++++++++++++++++++++--------------- 1 file changed, 155 insertions(+), 108 deletions(-) diff --git a/payjoin-client/src/main.rs b/payjoin-client/src/main.rs index 483b05b54..563b54f73 100644 --- a/payjoin-client/src/main.rs +++ b/payjoin-client/src/main.rs @@ -34,15 +34,24 @@ async fn main() -> Result<(), Error> { .help("The bitcoind rpc cookie file to use for authentication") .takes_value(true) .required(true)) + .arg(Arg::with_name("bip21") .short("b") .long("bip21") .help("The BIP21 URI to send to") - .takes_value(true)) + .takes_value(true) + .group("send")) + .arg(Arg::with_name("endpoint") + .short("e") + .long("endpoint") + .help("The endpoint to send the payjoin to") + .takes_value(true) + .group("send")) + .arg(Arg::with_name("relay") .short("r") .long("relay") - .help("PayJoin relay to establish p2p networking") + .help("PayJoin TURN server relay address to establish p2p networking") .takes_value(true)) .arg(Arg::with_name("amount") .short("a") @@ -68,127 +77,82 @@ async fn main() -> Result<(), Error> { if matches.is_present("relay") { let relay = matches.value_of("relay").unwrap(); - let amount = matches.value_of("amount").unwrap(); - // Ensure relay connection - // TURN client won't create a local listening socket by itself. - let conn = UdpSocket::bind("0.0.0.0:0").await?; - - let turn_server_addr = relay.to_string(); - - let cfg = ClientConfig { - stun_serv_addr: turn_server_addr.clone(), - turn_serv_addr: turn_server_addr, - username: "test".to_string(), - password: "test".to_string(), - realm: "test".to_string(), - software: String::new(), - rto_in_ms: 0, - conn: Arc::new(conn), - vnet: None, - }; - - let client = Client::new(cfg).await?; - - // Start listening on the conn provided. - client.listen().await?; - - // Allocate a relay socket on the TURN server. On success, it - // will return a net.PacketConn which represents the remote - // socket. - let relay_conn = client.allocate().await?; - - // The relayConn's local address is actually the transport - // address assigned on the TURN server. - println!("relayed-address={}", relay_conn.local_addr()?); - - // If you provided `-ping`, perform a ping test agaist the - let ping = true; - // relayConn we have just allocated. - if ping { - do_ping_test(&client, relay_conn).await?; - } - - receive_payjoin(relay, amount, bitcoind); - - client.close().await?; + listen_receiver(relay).await?; } else { - let bip21 = matches.value_of("bip21").unwrap().as_ref(); - send_payjoin(bip21, bitcoind); + //let bip21 = matches.value_of("bip21").unwrap().as_ref(); + let endpoint = matches.value_of("endpoint").unwrap(); + do_send(endpoint).await?; + //send_payjoin(bip21, bitcoind); } Ok(()) } -async fn do_ping_test( - client: &Client, - relay_conn: impl Conn + std::marker::Send + std::marker::Sync + 'static, -) -> Result<(), Error> { - // Send BindingRequest to learn our external IP - let mapped_addr = client.send_binding_request().await?; - - // Set up pinger socket (pingerConn) - //println!("bind..."); - let pinger_conn_tx = Arc::new(UdpSocket::bind("0.0.0.0:0").await?); - - // Punch a UDP hole for the relay_conn by sending a data to the mapped_addr. - // This will trigger a TURN client to generate a permission request to the - // TURN server. After this, packets from the IP address will be accepted by - // the TURN server. - //println!("relay_conn send hello to mapped_addr {}", mapped_addr); - relay_conn.send_to("Hello".as_bytes(), mapped_addr).await?; - let relay_addr = relay_conn.local_addr()?; +async fn listen_receiver(relay: &str) -> Result<(), Error> { + // 1. ConnectReceive + // Ensure relay connection + // TURN client won't create a local listening socket by itself. + let conn = UdpSocket::bind("0.0.0.0:0").await?; + + let turn_server_addr = relay.to_string(); + + let cfg = ClientConfig { + stun_serv_addr: turn_server_addr.clone(), + turn_serv_addr: turn_server_addr, + username: "receiver".to_string(), + password: "test".to_string(), + realm: "test".to_string(), + software: String::new(), + rto_in_ms: 0, + conn: Arc::new(conn), + vnet: None, + }; - let pinger_conn_rx = Arc::clone(&pinger_conn_tx); + let client = Client::new(cfg).await?; - // Start read-loop on pingerConn - tokio::spawn(async move { - let mut buf = vec![0u8; 1500]; - loop { - let (n, from) = match pinger_conn_rx.recv_from(&mut buf).await { - Ok((n, from)) => (n, from), - Err(_) => break, - }; + // Start listening on the conn provided. + client.listen().await?; - let msg = match String::from_utf8(buf[..n].to_vec()) { - Ok(msg) => msg, - Err(_) => break, - }; + // Allocate a relay socket on the TURN server. On success, it + // will return a net.PacketConn which represents the remote + // socket. + let relay_conn = client.allocate().await?; - println!("pingerConn read-loop: {} from {}", msg, from); - /*if sentAt, pingerErr := time.Parse(time.RFC3339Nano, msg); pingerErr == nil { - rtt := time.Since(sentAt) - log.Printf("%d bytes from from %s time=%d ms\n", n, from.String(), int(rtt.Seconds()*1000)) - }*/ - } - }); + // The relayConn's local address is actually the transport + // address assigned on the TURN server. + println!("relayed-address={}", relay_conn.local_addr()?); - // Start read-loop on relay_conn - tokio::spawn(async move { - let mut buf = vec![0u8; 1500]; - loop { - let (n, from) = match relay_conn.recv_from(&mut buf).await { - Err(_) => break, - Ok((n, from)) => (n, from), - }; + let mapped_addr = client.send_binding_request().await?; + println!("mapped-address={}", mapped_addr.to_string()); + // punch UDP hole. after this packets from the IP address will be accepted by the turn server + relay_conn.send_to("Hello".as_bytes(), mapped_addr).await?; - println!("relay_conn read-loop: {:?} from {}", &buf[..n], from); + // 2. Recv + listen_for_original_psbt(&client, relay_conn).await?; + //receive_payjoin(relay, amount, bitcoind); - // Echo back - if relay_conn.send_to(&buf[..n], from).await.is_err() { - break; - } - } - }); + client.close().await?; + Ok(()) +} +async fn listen_for_original_psbt( + client: &Client, + relay_conn: impl Conn + std::marker::Send + std::marker::Sync + 'static, +) -> Result<(), Error> { + let mut buf = [0u8; 1024]; + let (n, addr) = relay_conn.recv_from(&mut buf).await?; + println!("received {} bytes from {}", n, addr); + println!("received {}", String::from_utf8_lossy(&buf[..n])); + Ok(()) +} - tokio::time::sleep(Duration::from_millis(500)).await; +async fn do_send(relay_addr: &str) -> Result<(), Error> { + // Set up pinger socket (pingerConn) + //println!("bind..."); + let pinger_conn_tx = Arc::new(UdpSocket::bind("0.0.0.0:0").await?); + let pinger_conn_rx = Arc::clone(&pinger_conn_tx); - /*println!( - "pinger_conn_tx send 10 packets to relay addr {}...", - relay_addr - );*/ - // Send 10 packets from relay_conn to the echo server for _ in 0..2 { - let msg = "12345678910".to_owned(); //format!("{:?}", tokio::time::Instant::now()); + let msg = "Original PSBT".to_owned(); //format!("{:?}", tokio::time::Instant::now()); println!("sending msg={} with size={}", msg, msg.as_bytes().len()); pinger_conn_tx.send_to(msg.as_bytes(), relay_addr).await?; @@ -201,7 +165,7 @@ async fn do_ping_test( } fn receive_payjoin(relay: &str, amount: &str, client: bitcoincore_rpc::Client) { - + // register the payjoin endpoint with an allocation at the TURN relay // Receiver creates the bip21 payjoin URI let pj_receiver_address = client.get_new_address(None, None).unwrap(); let amount = Amount::from_str(amount).unwrap(); @@ -291,3 +255,86 @@ fn serialize_psbt(psbt: &Psbt) -> String { ) .unwrap() } + +// ** + +async fn do_ping_test( + client: &Client, + relay_conn: impl Conn + std::marker::Send + std::marker::Sync + 'static, +) -> Result<(), Error> { + // Send BindingRequest to learn our external IP + let mapped_addr = client.send_binding_request().await?; + + // Set up pinger socket (pingerConn) + //println!("bind..."); + let pinger_conn_tx = Arc::new(UdpSocket::bind("0.0.0.0:0").await?); + + // Punch a UDP hole for the relay_conn by sending a data to the mapped_addr. + // This will trigger a TURN client to generate a permission request to the + // TURN server. After this, packets from the IP address will be accepted by + // the TURN server. + //println!("relay_conn send hello to mapped_addr {}", mapped_addr); + relay_conn.send_to("\"Original PSBT\"".as_bytes(), mapped_addr).await?; + let relay_addr = relay_conn.local_addr()?; + + let pinger_conn_rx = Arc::clone(&pinger_conn_tx); + + // Start read-loop on pingerConn + tokio::spawn(async move { + let mut buf = vec![0u8; 1500]; + loop { + let (n, from) = match pinger_conn_rx.recv_from(&mut buf).await { + Ok((n, from)) => (n, from), + Err(_) => break, + }; + + let msg = match String::from_utf8(buf[..n].to_vec()) { + Ok(msg) => msg, + Err(_) => break, + }; + + println!("pingerConn read-loop: {} from {}", msg, from); + /*if sentAt, pingerErr := time.Parse(time.RFC3339Nano, msg); pingerErr == nil { + rtt := time.Since(sentAt) + log.Printf("%d bytes from from %s time=%d ms\n", n, from.String(), int(rtt.Seconds()*1000)) + }*/ + } + }); + + // Start read-loop on relay_conn + tokio::spawn(async move { + let mut buf = vec![0u8; 1500]; + loop { + let (n, from) = match relay_conn.recv_from(&mut buf).await { + Err(_) => break, + Ok((n, from)) => (n, from), + }; + + println!("relay_conn read-loop: {:?} from {}", &buf[..n], from); + + // Echo back + if relay_conn.send_to(&buf[..n], from).await.is_err() { + break; + } + } + }); + + tokio::time::sleep(Duration::from_millis(500)).await; + + /*println!( + "pinger_conn_tx send 10 packets to relay addr {}...", + relay_addr + );*/ + // Send 10 packets from relay_conn to the echo server + for _ in 0..2 { + let msg = "12345678910".to_owned(); //format!("{:?}", tokio::time::Instant::now()); + println!("sending msg={} with size={}", msg, msg.as_bytes().len()); + pinger_conn_tx.send_to(msg.as_bytes(), relay_addr).await?; + + // For simplicity, this example does not wait for the pong (reply). + // Instead, sleep 1 second. + tokio::time::sleep(Duration::from_secs(1)).await; + } + + Ok(()) +} \ No newline at end of file From 4e5375c78a1078475b736fc579bc508242b12f9b Mon Sep 17 00:00:00 2001 From: DanGould Date: Sat, 14 Jan 2023 12:50:24 -0500 Subject: [PATCH 08/24] Return relayed PayJoin Proposal PSBT placeholder --- payjoin-client/src/main.rs | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/payjoin-client/src/main.rs b/payjoin-client/src/main.rs index 563b54f73..54cebf250 100644 --- a/payjoin-client/src/main.rs +++ b/payjoin-client/src/main.rs @@ -123,12 +123,11 @@ async fn listen_receiver(relay: &str) -> Result<(), Error> { println!("relayed-address={}", relay_conn.local_addr()?); let mapped_addr = client.send_binding_request().await?; - println!("mapped-address={}", mapped_addr.to_string()); // punch UDP hole. after this packets from the IP address will be accepted by the turn server relay_conn.send_to("Hello".as_bytes(), mapped_addr).await?; // 2. Recv - listen_for_original_psbt(&client, relay_conn).await?; + listen_for_original_psbt(&client, relay_conn, mapped_addr).await?; //receive_payjoin(relay, amount, bitcoind); client.close().await?; @@ -137,11 +136,16 @@ async fn listen_receiver(relay: &str) -> Result<(), Error> { async fn listen_for_original_psbt( client: &Client, relay_conn: impl Conn + std::marker::Send + std::marker::Sync + 'static, + mapped_addr: std::net::SocketAddr, ) -> Result<(), Error> { let mut buf = [0u8; 1024]; - let (n, addr) = relay_conn.recv_from(&mut buf).await?; - println!("received {} bytes from {}", n, addr); + let (n, from) = relay_conn.recv_from(&mut buf).await?; + println!("received {} bytes from {}", n, from); println!("received {}", String::from_utf8_lossy(&buf[..n])); + + relay_conn.send_to("PayJoin Proposal PSBT".as_bytes(), from).await?; + println!("sent PayJin Proposal"); + Ok(()) } @@ -151,16 +155,15 @@ async fn do_send(relay_addr: &str) -> Result<(), Error> { let pinger_conn_tx = Arc::new(UdpSocket::bind("0.0.0.0:0").await?); let pinger_conn_rx = Arc::clone(&pinger_conn_tx); - for _ in 0..2 { - let msg = "Original PSBT".to_owned(); //format!("{:?}", tokio::time::Instant::now()); - println!("sending msg={} with size={}", msg, msg.as_bytes().len()); - pinger_conn_tx.send_to(msg.as_bytes(), relay_addr).await?; + let msg = "Original PSBT".to_owned(); //format!("{:?}", tokio::time::Instant::now()); + println!("sending msg={} with size={}", msg, msg.as_bytes().len()); + pinger_conn_tx.send_to(msg.as_bytes(), relay_addr).await?; - // For simplicity, this example does not wait for the pong (reply). - // Instead, sleep 1 second. - tokio::time::sleep(Duration::from_secs(1)).await; - } + let mut buf = [0u8; 1024]; + let (n, from) = pinger_conn_rx.recv_from(&mut buf).await?; + let msg = String::from_utf8(buf[..n].to_vec()).unwrap(); + println!("response: {} from {}", msg, from); Ok(()) } From 89b285ce1479fdc84816b0e212bacccf8fad6873 Mon Sep 17 00:00:00 2001 From: DanGould Date: Sat, 14 Jan 2023 15:29:38 -0500 Subject: [PATCH 09/24] Secure relay comms with noise NKpsk0 pattern --- Cargo.lock | 243 ++++++++++++++++-- Cargo.toml | 2 +- nkpsk0/Cargo.toml | 13 + nkpsk0/src/consts.rs | 31 +++ nkpsk0/src/error.rs | 39 +++ nkpsk0/src/lib.rs | 25 ++ nkpsk0/src/macros.rs | 9 + nkpsk0/src/noisesession.rs | 211 ++++++++++++++++ nkpsk0/src/prims.rs | 71 ++++++ nkpsk0/src/state.rs | 353 +++++++++++++++++++++++++++ nkpsk0/src/types.rs | 487 +++++++++++++++++++++++++++++++++++++ nkpsk0/src/utils.rs | 16 ++ payjoin-client/Cargo.toml | 1 + payjoin-client/src/main.rs | 59 +++-- payjoin-relay/Cargo.toml | 2 +- 15 files changed, 1530 insertions(+), 32 deletions(-) create mode 100644 nkpsk0/Cargo.toml create mode 100644 nkpsk0/src/consts.rs create mode 100644 nkpsk0/src/error.rs create mode 100644 nkpsk0/src/lib.rs create mode 100644 nkpsk0/src/macros.rs create mode 100644 nkpsk0/src/noisesession.rs create mode 100644 nkpsk0/src/prims.rs create mode 100644 nkpsk0/src/state.rs create mode 100644 nkpsk0/src/types.rs create mode 100644 nkpsk0/src/utils.rs diff --git a/Cargo.lock b/Cargo.lock index a9816cb48..15eff12ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "arrayvec" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" +dependencies = [ + "nodrop", +] + [[package]] name = "async-trait" version = "0.1.61" @@ -48,6 +57,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "autocfg" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" +dependencies = [ + "autocfg 1.1.0", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -211,6 +229,16 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "blake2-rfc" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" +dependencies = [ + "arrayvec", + "constant_time_eq", +] + [[package]] name = "block-buffer" version = "0.10.3" @@ -292,12 +320,27 @@ dependencies = [ "vec_map", ] +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags", +] + [[package]] name = "const_fn" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + [[package]] name = "cookie" version = "0.14.4" @@ -482,6 +525,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + [[package]] name = "futures" version = "0.3.25" @@ -611,6 +660,26 @@ dependencies = [ "tracing", ] +[[package]] +name = "hacl-star" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cda95def225a973b6515a15affa30c406fc324dba2b9b8e946d90754b05fbcb" +dependencies = [ + "hacl-star-sys", + "rand_core 0.4.2", +] + +[[package]] +name = "hacl-star-sys" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4289931eb1f8813fa8bb09b7223827603977e1da485c4b94cee4a7b22f45b83" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -635,6 +704,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "http" version = "0.2.8" @@ -739,7 +814,7 @@ version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ - "autocfg", + "autocfg 1.1.0", "hashbrown", ] @@ -803,7 +878,7 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" dependencies = [ - "autocfg", + "autocfg 1.1.0", "scopeguard", ] @@ -843,7 +918,7 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" dependencies = [ - "autocfg", + "autocfg 1.1.0", ] [[package]] @@ -859,7 +934,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" dependencies = [ "adler", - "autocfg", + "autocfg 1.1.0", ] [[package]] @@ -904,6 +979,24 @@ dependencies = [ "memoffset", ] +[[package]] +name = "nkpsk0" +version = "1.0.3" +dependencies = [ + "blake2-rfc", + "constant_time_eq", + "hacl-star", + "hex", + "rand 0.6.5", + "zeroize", +] + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + [[package]] name = "num_cpus" version = "1.15.0" @@ -958,7 +1051,7 @@ version = "0.9.80" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" dependencies = [ - "autocfg", + "autocfg 1.1.0", "cc", "libc", "pkg-config", @@ -998,7 +1091,7 @@ dependencies = [ "bitcoind", "env_logger", "log", - "rand", + "rand 0.8.5", "url", ] @@ -1009,6 +1102,7 @@ dependencies = [ "base64", "bitcoincore-rpc 0.14.0", "clap", + "nkpsk0", "payjoin", "reqwest", "tokio", @@ -1111,6 +1205,25 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" +dependencies = [ + "autocfg 0.1.8", + "libc", + "rand_chacha 0.1.1", + "rand_core 0.4.2", + "rand_hc", + "rand_isaac", + "rand_jitter", + "rand_os", + "rand_pcg", + "rand_xorshift", + "winapi", +] + [[package]] name = "rand" version = "0.8.5" @@ -1118,8 +1231,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" +dependencies = [ + "autocfg 0.1.8", + "rand_core 0.3.1", ] [[package]] @@ -1129,9 +1252,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", ] +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + [[package]] name = "rand_core" version = "0.6.4" @@ -1141,6 +1279,77 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rand_hc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rand_isaac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rand_jitter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" +dependencies = [ + "libc", + "rand_core 0.4.2", + "winapi", +] + +[[package]] +name = "rand_os" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +dependencies = [ + "cloudabi", + "fuchsia-cprng", + "libc", + "rand_core 0.4.2", + "rdrand", + "winapi", +] + +[[package]] +name = "rand_pcg" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" +dependencies = [ + "autocfg 0.1.8", + "rand_core 0.4.2", +] + +[[package]] +name = "rand_xorshift" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -1432,7 +1641,7 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" dependencies = [ - "autocfg", + "autocfg 1.1.0", ] [[package]] @@ -1531,7 +1740,7 @@ dependencies = [ "crc", "lazy_static", "md-5", - "rand", + "rand 0.8.5", "ring", "subtle", "thiserror", @@ -1690,7 +1899,7 @@ version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46" dependencies = [ - "autocfg", + "autocfg 1.1.0", "bytes", "libc", "memchr", @@ -1782,7 +1991,7 @@ dependencies = [ "futures", "log", "md-5", - "rand", + "rand 0.8.5", "ring", "stun", "thiserror", @@ -2009,7 +2218,7 @@ dependencies = [ "libc", "log", "nix", - "rand", + "rand 0.8.5", "thiserror", "tokio", "winapi", @@ -2175,6 +2384,12 @@ dependencies = [ "libc", ] +[[package]] +name = "zeroize" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" + [[package]] name = "zip" version = "0.5.13" diff --git a/Cargo.toml b/Cargo.toml index d8ffa521f..fb85cd16d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,2 @@ [workspace] -members = ["payjoin", "payjoin-client", "payjoin-relay"] +members = ["payjoin", "payjoin-client", "payjoin-relay", "nkpsk0"] diff --git a/nkpsk0/Cargo.toml b/nkpsk0/Cargo.toml new file mode 100644 index 000000000..b473c7365 --- /dev/null +++ b/nkpsk0/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "nkpsk0" +version = "1.0.3" +authors = ["Symbolic Software "] +edition = "2018" + +[dependencies] +blake2-rfc = "0.2.18" +constant_time_eq = "0.1.5" +hacl-star = "0.1.0" +hex = "0.4.3" +rand = "0.6.5" +zeroize = "1.3.0" \ No newline at end of file diff --git a/nkpsk0/src/consts.rs b/nkpsk0/src/consts.rs new file mode 100644 index 000000000..ab231b223 --- /dev/null +++ b/nkpsk0/src/consts.rs @@ -0,0 +1,31 @@ +/* ---------------------------------------------------------------- * + * CONSTANTS * + * ---------------------------------------------------------------- */ + +#![allow(non_snake_case, non_upper_case_globals)] +use hacl_star::{chacha20poly1305, curve25519}; + +pub const DHLEN: usize = curve25519::SECRET_LENGTH; +pub(crate) const HASHLEN: usize = 32; +pub(crate) const BLOCKLEN: usize = 64; +pub(crate) const EMPTY_HASH: [u8; DHLEN] = [0_u8; HASHLEN]; +pub(crate) const EMPTY_KEY: [u8; DHLEN] = [0_u8; DHLEN]; +pub const MAC_LENGTH: usize = chacha20poly1305::MAC_LENGTH; +pub(crate) const MAX_MESSAGE: usize = 0xFFFF; +pub(crate) const MAX_NONCE: u64 = u64::max_value(); +pub(crate) const NONCE_LENGTH: usize = chacha20poly1305::NONCE_LENGTH; +pub(crate) const ZEROLEN: [u8; 0] = [0_u8; 0]; +pub(crate) const forbidden_curve_values: [[u8; 32]; 12] = [ + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [224, 235, 122, 124, 59, 65, 184, 174, 22, 86, 227, 250, 241, 159, 196, 106, 218, 9, 141, 235, 156, 50, 177, 253, 134, 98, 5, 22, 95, 73, 184, 0], + [95, 156, 149, 188, 163, 80, 140, 36, 177, 208, 177, 85, 156, 131, 239, 91, 4, 68, 92, 196, 88, 28, 142, 134, 216, 34, 78, 221, 208, 159, 17, 87], + [236, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], + [237, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], + [238, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127], + [205, 235, 122, 124, 59, 65, 184, 174, 22, 86, 227, 250, 241, 159, 196, 106, 218, 9, 141, 235, 156, 50, 177, 253, 134, 98, 5, 22, 95, 73, 184, 128], + [76, 156, 149, 188, 163, 80, 140, 36, 177, 208, 177, 85, 156, 131, 239, 91, 4, 68, 92, 196, 88, 28, 142, 134, 216, 34, 78, 221, 208, 159, 17, 215], + [217, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [218, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], + [219, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255] +]; \ No newline at end of file diff --git a/nkpsk0/src/error.rs b/nkpsk0/src/error.rs new file mode 100644 index 000000000..03a570c08 --- /dev/null +++ b/nkpsk0/src/error.rs @@ -0,0 +1,39 @@ +#[derive(Debug)] +pub enum NoiseError { + DecryptionError, + UnsupportedMessageLengthError, + ExhaustedNonceError, + InvalidKeyError, + InvalidPublicKeyError, + EmptyKeyError, + InvalidInputError, + DerivePublicKeyFromEmptyKeyError, + Hex(hex::FromHexError), + MissingnsError, + MissingneError, + MissingHsMacError, + MissingrsError, + MissingreError +} + +impl std::fmt::Display for NoiseError { + #[inline] + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match *self { + NoiseError::DecryptionError => write!(f, "Unsuccesful decryption."), + NoiseError::UnsupportedMessageLengthError => write!(f, "Unsupported Message Length."), + NoiseError::ExhaustedNonceError => write!(f, "Reached maximum number of messages that can be sent for this session."), + NoiseError::DerivePublicKeyFromEmptyKeyError => write!(f, "Unable to derive PublicKey."), + NoiseError::InvalidKeyError => write!(f, "Invalid Key."), + NoiseError::InvalidPublicKeyError => write!(f, "Invalid Public Key."), + NoiseError::EmptyKeyError => write!(f, "Empty Key."), + NoiseError::InvalidInputError => write!(f, "Invalid input length."), + NoiseError::MissingnsError => write!(f, "Invalid message length."), + NoiseError::MissingHsMacError => write!(f, "Invalid message length."), + NoiseError::MissingneError => write!(f, "Invalid message length."), + NoiseError::MissingrsError => write!(f, "Invalid message length."), + NoiseError::MissingreError => write!(f, "Invalid message length."), + NoiseError::Hex(ref e) => e.fmt(f), + } + } +} \ No newline at end of file diff --git a/nkpsk0/src/lib.rs b/nkpsk0/src/lib.rs new file mode 100644 index 000000000..ed1dc3aad --- /dev/null +++ b/nkpsk0/src/lib.rs @@ -0,0 +1,25 @@ +/* +NKpsk0: + <- s + ... + -> psk, e, es + <- e, ee + -> + <- +*/ + +/* ---------------------------------------------------------------- * + * PARAMETERS * + * ---------------------------------------------------------------- */ + +#[macro_use] +pub(crate) mod macros; + +pub(crate) mod prims; +pub(crate) mod state; +pub(crate) mod utils; + +pub mod consts; +pub mod error; +pub mod noisesession; +pub mod types; \ No newline at end of file diff --git a/nkpsk0/src/macros.rs b/nkpsk0/src/macros.rs new file mode 100644 index 000000000..16d7bc9d4 --- /dev/null +++ b/nkpsk0/src/macros.rs @@ -0,0 +1,9 @@ +/* ---------------------------------------------------------------- * + * MACROS * + * ---------------------------------------------------------------- */ + +macro_rules! copy_slices { + ($inslice:expr, $outslice:expr) => { + $outslice[..$inslice.len()].clone_from_slice(&$inslice[..]) + }; +} diff --git a/nkpsk0/src/noisesession.rs b/nkpsk0/src/noisesession.rs new file mode 100644 index 000000000..d8a14fa03 --- /dev/null +++ b/nkpsk0/src/noisesession.rs @@ -0,0 +1,211 @@ +/* ---------------------------------------------------------------- * + * PROCESSES * + * ---------------------------------------------------------------- */ + +use crate::{consts::{HASHLEN, MAC_LENGTH, MAX_MESSAGE}, + error::NoiseError, + state::{CipherState, HandshakeState}, + types::{Hash, Keypair, Psk, PublicKey}}; +/// A `NoiseSession` object is used to keep track of the states of both local +/// and remote parties before, during, and after a handshake. +/// +/// It contains: +/// - `hs`: Keeps track of the local party's state while a handshake is being +/// performed. +/// - `h`: Stores the handshake hash output after a successful handshake in a +/// Hash object. Is initialized as array of 0 bytes. +/// - `cs1`: Keeps track of the local party's post-handshake state. Contains a +/// cryptographic key and a nonce. +/// - `cs2`: Keeps track of the remote party's post-handshake state. Contains a +/// cryptographic key and a nonce. +/// - `mc`: Keeps track of the total number of incoming and outgoing messages, +/// including those sent during a handshake. +/// - `i`: `bool` value that indicates whether this session corresponds to the +/// local or remote party. +/// - `is_transport`: `bool` value that indicates whether a handshake has been +/// performed succesfully with a remote session and the session is in transport mode. + +pub struct NoiseSession { + hs: HandshakeState, + h: Hash, + cs1: CipherState, + cs2: CipherState, + mc: u128, + i: bool, + is_transport: bool, +} +impl NoiseSession { + /// Returns `true` if a handshake has been successfully performed and the session is in transport mode, or false otherwise. + pub fn is_transport(&self) -> bool { + self.is_transport + } + + /// Clears `cs1`. + pub fn clear_local_cipherstate(&mut self) { + self.cs1.clear(); + } + + /// Clears `cs2`. + pub fn clear_remote_cipherstate(&mut self) { + self.cs2.clear(); + } + + /// Calls the [Rekey](https://noiseprotocol.org/noise.html#rekey) method for `cs1` + pub fn rekey_local_cipherstate(&mut self) { + self.cs1.rekey() + } + + /// Calls the [Rekey](https://noiseprotocol.org/noise.html#rekey) method for `cs2` + pub fn rekey_remote_cipherstate(&mut self) { + self.cs1.rekey() + } + + /// `NoiseSession` destructor function. + pub fn end_session(mut self) { + self.hs.clear(); + self.clear_local_cipherstate(); + self.clear_remote_cipherstate(); + self.cs2.clear(); + self.mc = 0; + self.h = Hash::new(); + } + + /// Returns `h`. + pub fn get_handshake_hash(&self) -> Option<[u8; HASHLEN]> { + if self.is_transport { + return Some(self.h.as_bytes()); + } + None + } + + /// Returns `mc`. + pub fn get_message_count(&self) -> u128 { + self.mc + } + + /// Sets the value of the local ephemeral keypair as the parameter `e`. + pub fn set_ephemeral_keypair(&mut self, e: Keypair) { + self.hs.set_ephemeral_keypair(e); + } + + /// Returns a `Option` object that contains the remote party's static `PublicKey`. + /// Note that this function returns `None` before a handshake is successfuly performed and + /// the session is in transport mode. + pub fn get_remote_static_public_key(&self) -> Option { + if self.is_transport { + return Some(self.hs.get_remote_static_public_key()); + } + None + } + + + /// Instantiates a `NoiseSession` object. Takes the following as parameters: + /// - `initiator`: `bool` variable. To be set as `true` when initiating a handshake with a remote party, or `false` otherwise. + /// - `prologue`: `Message` object. Could optionally contain the name of the protocol to be used. + /// - `s`: `Keypair` object. Contains local party's static keypair. + /// - `rs`: `Option`. Contains the remote party's static public key. Tip: use `Some(rs_value)` in case a remote static key exists and `None` otherwise. + /// - `psk`: `Psk` object. Contains the pre-shared key. + pub fn init_session(initiator: bool, prologue: &[u8], s: Keypair, rs: Option, psk: Psk) -> NoiseSession { + if initiator { + NoiseSession{ + hs: HandshakeState::initialize_initiator(prologue, s, rs.unwrap_or(PublicKey::empty()), psk), + mc: 0, + i: initiator, + cs1: CipherState::new(), + cs2: CipherState::new(), + h: Hash::new(), + is_transport: false, + } + } else { + NoiseSession { + hs: HandshakeState::initialize_responder(prologue, s, psk), + mc: 0, + i: initiator, + cs1: CipherState::new(), + cs2: CipherState::new(), + h: Hash::new(), + is_transport: false, + } + } + } + + /// Takes a `&mut [u8]` containing plaintext as a parameter. + /// This method returns a `Ok(()))` upon successful encryption, and `Err(NoiseError)` otherwise + /// _Note that for security reasons and for better performance, `send_message` overwrites the bytes containing the plaintext with the ciphertext. For this reason and to account for the fact that ciphertext and handshake messages encapsulate important values, a pattern specific padding of zero bytes must be added to the following messages. + /// For transport messages: + /// All messages must be appended with 16 empty bytes that act as a placeholder for the MAC (Message Authentication Code). These 16 bytes will be overwritten by `send_message` + /// For handshake messages: + /// Kindly use the message lengths listed in the test file under `../tests/handshake.rs`, where examples and notes are also provided. + /// _Also Note that while `is_transport` is false the ciphertext will be included as a payload for handshake messages and thus will not offer the same guarantees offered by post-handshake messages._ + pub fn send_message(&mut self, in_out: &mut [u8]) -> Result<(), NoiseError> { + if in_out.len() < MAC_LENGTH || in_out.len() > MAX_MESSAGE { + return Err(NoiseError::UnsupportedMessageLengthError); + } + println!("a"); + if self.mc == 0 { + self.hs.write_message_a(in_out)?; + println!("b"); + + } + else if self.mc == 1 { + let temp = self.hs.write_message_b(in_out)?; + self.h = temp.0; + self.is_transport = true; + self.cs1 = temp.1; + self.cs2 = temp.2; + self.hs.clear(); + println!("c"); + + } else if self.i { + self.cs1.write_message_regular(in_out)?; + println!("d"); + + } else { + self.cs2.write_message_regular(in_out)?; + println!("e"); + + } + self.mc += 1; + Ok(()) + } + + /// Takes a `&mut [u8]` received from the remote party as a parameter. + /// This method returns a `Ok(()))` upon successful decrytion. and `Err(NoiseError)` otherwise. + /// _Note that for security reasons and for better performance, `recv_message` overwrites the bytes containing the ciphertext with the plaintext and clears the MAC from them last 16 bytes of the message, and other keys that might be encapsulated while performing a handshake. + /// For transport messages: + /// You should expect to find the plaintext in the same array you passed a reference of as a parameter. The last 16 bytes of this array will be zero bytes and can be safely ignored. + /// For handshake messages: + /// Kindly use the message lengths listed in the test file under `../tests/handshake.rs`, where examples and notes are also provided. + /// + /// _Note that while `is_transport` is false the ciphertext will be included as a payload for handshake messages and thus will not offer the same guarantees offered by post-handshake messages._ + pub fn recv_message(&mut self, in_out: &mut [u8]) -> Result<(), NoiseError> { + if in_out.len() < MAC_LENGTH || in_out.len() > MAX_MESSAGE { + return Err(NoiseError::UnsupportedMessageLengthError); + } + println!("a"); + if self.mc == 0 { + self.hs.read_message_a(in_out)?; + println!("b"); + } + else if self.mc == 1 { + let temp = self.hs.read_message_b(in_out)?; + self.h = temp.0; + self.is_transport = true; + self.cs1 = temp.1; + self.cs2 = temp.2; + self.hs.clear(); + println!("c"); + + } else if self.i { + self.cs2.read_message_regular(in_out)?; + println!("d"); + + } else { + self.cs1.read_message_regular(in_out)?; + println!("e"); + + } + self.mc += 1; + Ok(()) + } +} diff --git a/nkpsk0/src/prims.rs b/nkpsk0/src/prims.rs new file mode 100644 index 000000000..13137943c --- /dev/null +++ b/nkpsk0/src/prims.rs @@ -0,0 +1,71 @@ +/* ---------------------------------------------------------------- * + * PRIMITIVES * + * ---------------------------------------------------------------- */ + +use crate::{consts::{BLOCKLEN, DHLEN, EMPTY_HASH, HASHLEN, MAC_LENGTH}, + utils::{from_slice_hashlen, prep_nonce}}; +use blake2_rfc::blake2s::Blake2s; +use hacl_star::chacha20poly1305; + +pub(crate) fn encrypt(k: [u8; DHLEN], n: u64, ad: &[u8], in_out: &mut [u8], mac: &mut [u8; MAC_LENGTH]) { + chacha20poly1305::key(&k).nonce(&prep_nonce(n)).encrypt(ad, in_out, mac) +} + +pub(crate) fn decrypt(k: [u8; DHLEN], n: u64, ad: &[u8], in_out: &mut [u8], mac: &[u8; MAC_LENGTH]) -> bool { + chacha20poly1305::key(&k).nonce(&prep_nonce(n)).decrypt(ad, in_out, mac) +} + +pub(crate) fn hash(data: &[u8]) -> [u8; HASHLEN] { + let mut context = Blake2s::new(HASHLEN); + context.update(data); + let hash = context.finalize(); + from_slice_hashlen(&hash.as_bytes()[..]) +} + +pub(crate) fn hash_with_context(con: &[u8], data: &[u8]) -> [u8; HASHLEN] { + let mut context = Blake2s::new(HASHLEN); + context.update(con); + context.update(data); + let hash = context.finalize(); + from_slice_hashlen(&hash.as_bytes()[..]) +} + +pub(crate) fn hmac(key: &[u8], data: &[u8], out: &mut [u8]) { + let mut context = Blake2s::new(HASHLEN); + let mut ipad = [0x36_u8; BLOCKLEN]; + let mut opad = [0x5c_u8; BLOCKLEN]; + for count in 0..key.len() { + ipad[count] ^= key[count]; + opad[count] ^= key[count]; + } + context.update(&ipad[..BLOCKLEN]); + context.update(data); + let inner_output = context.finalize(); + context = Blake2s::new(HASHLEN); + context.update(&opad[..BLOCKLEN]); + context.update(&inner_output.as_bytes()[..HASHLEN]); + out.copy_from_slice(context.finalize().as_bytes()); +} + +pub(crate) fn hkdf( + chaining_key: &[u8], input_key_material: &[u8], outputs: usize, out1: &mut [u8], + out2: &mut [u8], out3: &mut [u8], +) { + let mut temp_key = EMPTY_HASH; + hmac(chaining_key, input_key_material, &mut temp_key); + hmac(&temp_key, &[1u8,], out1); + if outputs == 1 { + return; + } + let mut in2 = [0_u8; HASHLEN + 1]; + copy_slices!(&out1[0..HASHLEN], &mut in2); + in2[HASHLEN] = 2; + hmac(&temp_key, &in2[..=HASHLEN], out2); + if outputs == 2 { + return; + } + let mut in3 = [0_u8; HASHLEN + 1]; + copy_slices!(&out2[0..HASHLEN], &mut in3); + in3[HASHLEN] = 3; + hmac(&temp_key, &in3[..=HASHLEN], out3); +} diff --git a/nkpsk0/src/state.rs b/nkpsk0/src/state.rs new file mode 100644 index 000000000..9258184a4 --- /dev/null +++ b/nkpsk0/src/state.rs @@ -0,0 +1,353 @@ +/* ---------------------------------------------------------------- * + * STATE MANAGEMENT * + * ---------------------------------------------------------------- */ + +use crate::{consts::{DHLEN, EMPTY_HASH, EMPTY_KEY, HASHLEN, MAC_LENGTH, NONCE_LENGTH, ZEROLEN}, + error::NoiseError, + prims::{decrypt, encrypt, hash, hash_with_context, hkdf}, + types::{Hash, Key, Keypair, Nonce, Psk, PublicKey}, + utils::from_slice_hashlen}; +use hacl_star::chacha20poly1305; +use zeroize::Zeroize; + +pub(crate) struct CipherState { + k: Key, + n: Nonce, +} + +impl CipherState { + pub(crate) fn new() -> Self { + Self::from_key(Key::new()) + } + + pub(crate) fn clear_key(&mut self) { + self.k.clear(); + } + + pub(crate) fn clear(&mut self) { + self.k.clear(); + self.n = Nonce::new(); + } + + pub(crate) fn from_key(key: Key) -> Self { + let nonce: Nonce = Nonce::new(); + Self { + k: key, + n: nonce, + } + } + + pub(crate) fn has_key(&self) -> bool { + !self.k.is_empty() + } + + #[allow(dead_code)] + pub(crate) fn set_nonce(&mut self, n: Nonce) { + self.n = n; + } + + #[allow(dead_code)] + pub(crate) fn get_nonce(&self) -> Nonce { + self.n + } + + pub(crate) fn encrypt_with_ad( + &mut self, ad: &[u8], in_out: &mut [u8], mac: &mut [u8; MAC_LENGTH]) -> Result<(), NoiseError> { + let nonce = self.n.get_value()?; + if self.has_key() { + encrypt(from_slice_hashlen(&self.k.as_bytes()[..]), nonce, ad, in_out, mac); + self.n.increment(); + return Ok(()); + } + Err(NoiseError::EmptyKeyError) + } + + pub(crate) fn decrypt_with_ad( + &mut self, ad: &[u8], in_out: &mut [u8], mac: &mut [u8; MAC_LENGTH] + ) -> Result<(), NoiseError> { + let nonce = self.n.get_value()?; + if self.has_key() { + if decrypt(from_slice_hashlen(&self.k.as_bytes()[..]), nonce, ad, in_out, mac) { + self.n.increment(); + return Ok(()); + } + return Err(NoiseError::DecryptionError); + } + Err(NoiseError::EmptyKeyError) + } + + #[allow(dead_code)] + pub(crate) fn rekey(&mut self) { + let mut in_out = EMPTY_KEY; + chacha20poly1305::key(&self.k.as_bytes()).nonce(&[0xFFu8; NONCE_LENGTH]).encrypt( + &ZEROLEN[..], + &mut in_out[..], + &mut [0u8; 16], + ); + self.k.clear(); + self.k = Key::from_bytes(in_out); + in_out.zeroize(); + } + + pub(crate) fn write_message_regular( + &mut self, in_out: &mut [u8] + ) -> Result<(), NoiseError> { + let (in_out, mac) = in_out.split_at_mut(in_out.len()-MAC_LENGTH); + let mut temp_mac: [u8; MAC_LENGTH] = [0u8; MAC_LENGTH]; + self.encrypt_with_ad(&ZEROLEN[..], in_out, &mut temp_mac)?; + mac.copy_from_slice(&temp_mac[..]); + Ok(()) + } + + pub(crate) fn read_message_regular( + &mut self, in_out: &mut [u8] + ) -> Result<(), NoiseError> { + let (in_out, mac) = in_out.split_at_mut(in_out.len()-MAC_LENGTH); + let mut temp_mac: [u8; MAC_LENGTH] = [0u8; MAC_LENGTH]; + temp_mac.copy_from_slice(mac); + self.decrypt_with_ad(&ZEROLEN[..], in_out, &mut temp_mac)?; + temp_mac.zeroize(); + Ok(()) + } +} + +pub struct SymmetricState { + cs: CipherState, + ck: Hash, + h: Hash, +} + +impl SymmetricState { + pub(crate) fn clear(&mut self) { + self.cs.clear_key(); + self.ck.clear(); + } + + pub fn initialize_symmetric(protocol_name: &[u8]) -> Self { + let h: Hash; + match protocol_name.len() { + 0..=31 => { + let mut temp = [0u8; HASHLEN]; + let (protocol_name_len, _) = temp.split_at_mut(protocol_name.len()); + protocol_name_len.copy_from_slice(protocol_name); + h = Hash::from_bytes(from_slice_hashlen(&temp[..])); + }, + 32 => h = Hash::from_bytes(from_slice_hashlen(protocol_name)), + _ => h = Hash::from_bytes(hash(protocol_name)), + } + let ck: Hash = Hash::from_bytes(from_slice_hashlen(&h.as_bytes()[..])); + let cs: CipherState = CipherState::new(); + Self { + cs, + ck, + h, + } + } + + pub(crate) fn mix_key(&mut self, input_key_material: &[u8]) { + let mut out0: [u8; HASHLEN] = EMPTY_HASH; + let mut out1: [u8; HASHLEN] = EMPTY_HASH; + let mut out2: [u8; HASHLEN] = EMPTY_HASH; + hkdf( + &self.ck.as_bytes()[..], + input_key_material, + 2, + &mut out0[..], + &mut out1[..], + &mut out2[..], + ); + self.ck = Hash::from_bytes(out0); + let mut temp_k: [u8; 32] = EMPTY_KEY; + temp_k.copy_from_slice(&out1[..32]); + self.cs = CipherState::from_key(Key::from_bytes(temp_k)); + } + + pub(crate) fn mix_hash(&mut self, data: &[u8]) { + self.h = Hash::from_bytes(hash_with_context(&self.h.as_bytes()[..], data)); + } + + #[allow(dead_code)] + pub(crate) fn mix_key_and_hash(&mut self, input_key_material: &[u8]) { + let mut out0: [u8; HASHLEN] = EMPTY_HASH; + let mut out1: [u8; HASHLEN] = EMPTY_HASH; + let mut out2: [u8; HASHLEN] = EMPTY_HASH; + hkdf( + &self.ck.as_bytes()[..], + input_key_material, + 3, + &mut out0[..], + &mut out1[..], + &mut out2[..], + ); + self.ck = Hash::from_bytes(out0); + let temp_h: [u8; HASHLEN] = out1; + let mut temp_k: [u8; DHLEN] = out2; + self.mix_hash(&temp_h[..]); + temp_k.copy_from_slice(&out2[..32]); + self.cs = CipherState::from_key(Key::from_bytes(temp_k)); + out0.zeroize(); + out1.zeroize(); + out2.zeroize(); + } + + #[allow(dead_code)] + pub(crate) fn get_handshake_hash(&self) -> [u8; HASHLEN] { + from_slice_hashlen(&self.h.as_bytes()[..]) + } + + pub(crate) fn encrypt_and_hash(&mut self, in_out: &mut [u8]) -> Result<(), NoiseError> { + let mut temp_mac: [u8; MAC_LENGTH] = [0u8;MAC_LENGTH]; + let (plaintext, mac) = in_out.split_at_mut(in_out.len()-MAC_LENGTH); + self.cs.encrypt_with_ad(&self.h.as_bytes()[..], plaintext, &mut temp_mac)?; + mac.copy_from_slice(&temp_mac[..]); + self.mix_hash(in_out); + Ok(()) + } + + pub(crate) fn decrypt_and_hash(&mut self, in_out: &mut [u8]) -> Result<(), NoiseError> { + let mut temp: [u8; 2048] = [0u8; 2048]; + temp[..in_out.len()].copy_from_slice(in_out); + let (ciphertext, mac) = in_out.split_at_mut(in_out.len()-MAC_LENGTH); + let mut temp_mac: [u8; MAC_LENGTH] = [0u8;MAC_LENGTH]; + temp_mac.copy_from_slice(mac); + self.cs.decrypt_with_ad(&self.h.as_bytes()[..], ciphertext, &mut temp_mac)?; + self.mix_hash(&temp[..in_out.len()]); + Ok(()) + } + + pub(crate) fn split(&mut self) -> (CipherState, CipherState) { + let mut temp_k1: [u8; HASHLEN] = EMPTY_HASH; + let mut temp_k2: [u8; HASHLEN] = EMPTY_HASH; + let mut out2: [u8; HASHLEN] = EMPTY_HASH; + hkdf( + &self.ck.as_bytes()[..], + &ZEROLEN[..], + 2, + &mut temp_k1[..], + &mut temp_k2[..], + &mut out2[..], + ); + let cs1: CipherState = + CipherState::from_key(Key::from_bytes(from_slice_hashlen(&temp_k1[..32]))); + temp_k1.copy_from_slice(&EMPTY_HASH[..]); + let cs2: CipherState = + CipherState::from_key(Key::from_bytes(from_slice_hashlen(&temp_k2[..32]))); + temp_k2.copy_from_slice(&EMPTY_HASH[..]); + (cs1, cs2) + } +} + + +pub struct HandshakeState { + ss: SymmetricState, + s: Keypair, + e: Keypair, + rs: PublicKey, + re: PublicKey, + psk: Psk, +} + +impl HandshakeState { + pub(crate) fn clear(&mut self) { + self.s.clear(); + self.e.clear(); + self.re.clear(); + self.psk.clear(); + } + + pub fn get_remote_static_public_key(&self) -> PublicKey { + self.rs + } + + pub(crate) fn set_ephemeral_keypair(&mut self, e: Keypair) { + self.e = e; + } + + pub(crate) fn initialize_initiator(prologue: &[u8], s: Keypair, rs: PublicKey, psk: Psk) -> HandshakeState { + let protocol_name = b"Noise_NKpsk0_25519_ChaChaPoly_BLAKE2s"; + let mut ss: SymmetricState = SymmetricState::initialize_symmetric(&protocol_name[..]); + ss.mix_hash(prologue); + ss.mix_hash(&rs.as_bytes()[..]); + HandshakeState{ss, s, e: Keypair::new_empty(), rs, re: PublicKey::empty(), psk} + } + + pub(crate) fn initialize_responder(prologue: &[u8], s: Keypair, psk: Psk) -> HandshakeState { + let protocol_name = b"Noise_NKpsk0_25519_ChaChaPoly_BLAKE2s"; + let mut ss: SymmetricState = SymmetricState::initialize_symmetric(&protocol_name[..]); + ss.mix_hash(prologue); + let rs = PublicKey::empty(); + ss.mix_hash(&s.get_public_key().as_bytes()[..]); + HandshakeState{ss, s, e: Keypair::new_empty(), rs, re: PublicKey::empty(), psk} + } + pub(crate) fn write_message_a(&mut self, in_out: &mut [u8]) -> Result<(), NoiseError> { + self.ss.mix_key_and_hash(&self.psk.as_bytes()); + if in_out.len() < DHLEN { + return Err(NoiseError::MissingneError); + } + if self.e.is_empty() { + self.e = Keypair::default(); + } + let (ne, in_out) = in_out.split_at_mut(DHLEN); + ne.copy_from_slice(&self.e.get_public_key().as_bytes()[..]); + self.ss.mix_hash(ne); + self.ss.mix_key(&self.e.get_public_key().as_bytes()); + self.ss.mix_key(&self.e.dh(&self.rs.as_bytes())); + self.ss.encrypt_and_hash(in_out)?; + Ok(()) + } + + pub(crate) fn write_message_b(&mut self, in_out: &mut [u8]) -> Result<(Hash, CipherState, CipherState), NoiseError> { + if in_out.len() < DHLEN { + return Err(NoiseError::MissingneError); + } + if self.e.is_empty() { + self.e = Keypair::default(); + } + let (ne, in_out) = in_out.split_at_mut(DHLEN); + ne.copy_from_slice(&self.e.get_public_key().as_bytes()[..]); + self.ss.mix_hash(ne); + self.ss.mix_key(&self.e.get_public_key().as_bytes()); + self.ss.mix_key(&self.e.dh(&self.re.as_bytes())); + self.ss.encrypt_and_hash(in_out)?; + let h: Hash = Hash::from_bytes(from_slice_hashlen(&self.ss.h.as_bytes())); + let (cs1, cs2) = self.ss.split(); + self.ss.clear(); + Ok((h, cs1, cs2)) + } + + + pub(crate) fn read_message_a(&mut self, in_out: &mut [u8]) -> Result<(), NoiseError> { + self.ss.mix_key_and_hash(&self.psk.as_bytes()); + if in_out.len() < MAC_LENGTH+DHLEN { + return Err(NoiseError::MissingreError); + } + let (re, in_out) = in_out.split_at_mut(DHLEN); + self.re = PublicKey::from_bytes(from_slice_hashlen(re))?; + self.ss.mix_hash(&self.re.as_bytes()[..DHLEN]); + self.ss.mix_key(&self.re.as_bytes()); + self.ss.mix_key(&self.s.dh(&self.re.as_bytes())); + println!("dec_&_hash"); + self.ss.decrypt_and_hash(in_out)?; + println!("after dec_&_hash"); + + Ok(()) + } + + pub(crate) fn read_message_b(&mut self, in_out: &mut [u8]) -> Result<(Hash, CipherState, CipherState), NoiseError> { + if in_out.len() < MAC_LENGTH+DHLEN { + return Err(NoiseError::MissingreError); + } + let (re, in_out) = in_out.split_at_mut(DHLEN); + self.re = PublicKey::from_bytes(from_slice_hashlen(re))?; + self.ss.mix_hash(&self.re.as_bytes()[..DHLEN]); + self.ss.mix_key(&self.re.as_bytes()); + self.ss.mix_key(&self.e.dh(&self.re.as_bytes())); + self.ss.decrypt_and_hash(in_out)?; + let h: Hash = Hash::from_bytes(from_slice_hashlen(&self.ss.h.as_bytes())); + let (cs1, cs2) = self.ss.split(); + self.ss.clear(); + Ok((h, cs1, cs2)) + } + + +} diff --git a/nkpsk0/src/types.rs b/nkpsk0/src/types.rs new file mode 100644 index 000000000..f0977b32e --- /dev/null +++ b/nkpsk0/src/types.rs @@ -0,0 +1,487 @@ +/* ---------------------------------------------------------------- * + * TYPES * + * ---------------------------------------------------------------- */ +use constant_time_eq::constant_time_eq; +use crate::{ + consts::{DHLEN, EMPTY_KEY, HASHLEN, MAX_NONCE, forbidden_curve_values}, + error::NoiseError, +}; +use hacl_star::curve25519; +use rand; +use zeroize::Zeroize; + +fn decode_str_32(s: &str) -> Result<[u8; DHLEN], NoiseError> { + if let Ok(x) = hex::decode(s) { + if x.len() == DHLEN { + let mut temp: [u8; DHLEN] = [0_u8; DHLEN]; + temp.copy_from_slice(&x[..]); + Ok(temp) + } + else { + return Err(NoiseError::InvalidInputError); + } + } + else { + return Err(NoiseError::InvalidInputError); + } +} + +#[derive(Clone)] +pub(crate) struct Hash { + h: [u8; HASHLEN], +} +impl Hash { + pub(crate) fn clear(&mut self) { + self.h.zeroize(); + } + pub(crate) fn from_bytes(hash: [u8; HASHLEN]) -> Self { + Self { + h: hash, + } + } + pub(crate) fn as_bytes(&self) -> [u8; DHLEN] { + self.h + } + pub(crate) fn new() -> Self { + Self::from_bytes([0_u8; HASHLEN]) + } +} + +#[derive(Clone, Default)] +pub struct Key { + k: [u8; DHLEN], +} +impl Key { + pub(crate) fn clear(&mut self) { + self.k.zeroize(); + } + /// Instanciates a new empty `Key`. + pub fn new() -> Self { + Self::from_bytes(EMPTY_KEY) + } + /// Instanciates a new `Key` from an array of `DHLEN` bytes. + pub fn from_bytes(key: [u8; DHLEN]) -> Self { + Self { + k: key, + } + } + pub(crate) fn as_bytes(&self) -> [u8; DHLEN] { + self.k + } + /// Checks whether a `Key` object is empty or not. + /// # Example + /// + /// ``` + /// # use noiseexplorer_nkpsk0::{ + /// # error::NoiseError, + /// # types::Key, + /// # }; + /// # use std::str::FromStr; + /// # fn try_main() -> Result<(), NoiseError> { + /// let empty_key1 = Key::from_str("0000000000000000000000000000000000000000000000000000000000000000")?; + /// let empty_key2 = Key::new(); + /// let k = Key::from_str("4a3acbfdb163dec651dfa3194dece676d437029c62a408b4c5ea9114246e4893")?; + /// assert!(empty_key1.is_empty()); + /// assert!(empty_key2.is_empty()); + /// assert!(!k.is_empty()); + /// # Ok(()) + /// # } + /// # fn main() { + /// # try_main().unwrap(); + /// # } + /// ``` + pub fn is_empty(&self) -> bool { + constant_time_eq(&self.k[..], &EMPTY_KEY) + } + /// Derives a `PublicKey` from the `Key` and returns it. + pub fn generate_public_key(private_key: &[u8; DHLEN]) -> PublicKey { + let mut output: [u8; DHLEN] = EMPTY_KEY; + output.copy_from_slice(private_key); + let output = curve25519::SecretKey(output).get_public(); + PublicKey { + k: output.0, + } + } +} +impl std::str::FromStr for Key { + type Err = NoiseError; + /// Instanciates a new `Key` from a string of hexadecimal values. + /// # Example + /// + /// ``` + /// # use noiseexplorer_nkpsk0::{ + /// # error::NoiseError, + /// # types::Key, + /// # }; + /// # use std::str::FromStr; + /// # fn try_main() -> Result<(), NoiseError> { + /// let k = Key::from_str("4a3acbfdb163dec651dfa3194dece676d437029c62a408b4c5ea9114246e4893")?; + /// # Ok(()) + /// # } + /// # fn main() { + /// # try_main().unwrap(); + /// # } + /// ``` + fn from_str(key: &str) -> Result { + let a = decode_str_32(key)?; + Ok(Self::from_bytes(a)) + } +} + +pub struct Psk { + psk: [u8; DHLEN], +} +impl Psk { + /// Instanciates a new empty `Psk`. + pub fn default() -> Self { + Self::from_bytes(EMPTY_KEY) + } + pub(crate) fn clear(&mut self) { + self.psk.zeroize(); + } + /// Instanciates a new `Psk` from an array of `DHLEN` bytes. + pub fn from_bytes(k: [u8; DHLEN]) -> Self { + Self { + psk: k, + } + } + + #[allow(dead_code)] + pub(crate) fn as_bytes(&self) -> [u8; DHLEN] { + self.psk + } + /// Checks whether a `Psk` object is empty or not. + /// # Example + /// + /// ``` + /// # use noiseexplorer_nkpsk0::{ + /// # error::NoiseError, + /// # types::Psk, + /// # }; + /// # use std::str::FromStr; + /// # fn try_main() -> Result<(), NoiseError> { + /// let empty_key1 = Psk::from_str("0000000000000000000000000000000000000000000000000000000000000000")?; + /// let empty_key2 = Psk::default(); + /// let k = Psk::from_str("4a3acbfdb163dec651dfa3194dece676d437029c62a408b4c5ea9114246e4893")?; + /// assert!(empty_key1.is_empty()); + /// assert!(empty_key2.is_empty()); + /// assert!(!k.is_empty()); + /// # Ok(()) + /// # } + /// # fn main() { + /// # try_main().unwrap(); + /// # } + /// ``` + pub fn is_empty(&self) -> bool { + constant_time_eq(&self.psk[..], &EMPTY_KEY) + } +} +impl Clone for Psk { + fn clone(&self) -> Self { + Self { + psk: self.as_bytes().to_owned(), + } + } +} + +impl std::str::FromStr for Psk { + type Err = NoiseError; + /// Instanciates a new `Psk` from a string of hexadecimal values. + /// # Example + /// + /// ``` + /// # use noiseexplorer_nkpsk0::{ + /// # error::NoiseError, + /// # types::Psk, + /// # }; + /// # use std::str::FromStr; + /// # fn try_main() -> Result<(), NoiseError> { + /// let k = Psk::from_str("4a3acbfdb163dec651dfa3194dece676d437029c62a408b4c5ea9114246e4893")?; + /// # Ok(()) + /// # } + /// # fn main() { + /// # try_main().unwrap(); + /// # } + /// ``` + fn from_str(k: &str) -> Result { + let psk = decode_str_32(k)?; + if psk.len() > 32 { + + } + Ok(Self::from_bytes(psk)) + } +} + +pub struct PrivateKey { + k: [u8; DHLEN], +} +impl PrivateKey { + pub(crate) fn clear(&mut self) { + self.k.zeroize(); + } + /// Instanciates a new empty `PrivateKey`. + pub fn empty() -> Self { + Self { + k: EMPTY_KEY, + } + } + /// Instanciates a new `PrivateKey` from an array of `DHLEN` bytes. + pub fn from_bytes(k: [u8; DHLEN]) -> Self { + Self::from_hacl_secret_key(curve25519::SecretKey(k)) + } + pub(crate) fn from_hacl_secret_key(hacl_secret: curve25519::SecretKey) -> Self { + Self { + k: hacl_secret.0, + } + } + pub(crate) fn as_bytes(&self) -> [u8; DHLEN] { + self.k + } + /// Checks whether a `PrivateKey` object is empty or not. + /// # Example + /// + /// ``` + /// # use noiseexplorer_nkpsk0::{ + /// # error::NoiseError, + /// # types::PrivateKey, + /// # }; + /// # use std::str::FromStr; + /// # fn try_main() -> Result<(), NoiseError> { + /// let empty_key1 = PrivateKey::from_str("0000000000000000000000000000000000000000000000000000000000000000")?; + /// let empty_key2 = PrivateKey::empty(); + /// let k = PrivateKey::from_str("4a3acbfdb163dec651dfa3194dece676d437029c62a408b4c5ea9114246e4893")?; + /// assert!(empty_key1.is_empty()); + /// assert!(empty_key2.is_empty()); + /// assert!(!k.is_empty()); + /// # Ok(()) + /// # } + /// # fn main() { + /// # try_main().unwrap(); + /// # } + /// ``` + pub fn is_empty(&self) -> bool { + constant_time_eq(&self.k[..], &EMPTY_KEY) + } + /// Derives a `PublicKey` from the `PrivateKey` then returns `Ok(PublicKey)` when successful and `Err(NoiseError)` otherwise. + pub fn generate_public_key(&self) -> Result { + if self.is_empty() { + return Err(NoiseError::InvalidKeyError); + } + Ok(PublicKey { + k: curve25519::SecretKey(self.k).get_public().0, + }) + } +} + +impl std::str::FromStr for PrivateKey { + type Err = NoiseError; + /// Instanciates a new `PrivateKey` from a string of hexadecimal values. + /// # Example + /// + /// ``` + /// # use noiseexplorer_nkpsk0::{ + /// # error::NoiseError, + /// # types::PrivateKey, + /// # }; + /// # use std::str::FromStr; + /// # fn try_main() -> Result<(), NoiseError> { + /// let k = PrivateKey::from_str("4a3acbfdb163dec651dfa3194dece676d437029c62a408b4c5ea9114246e4893")?; + /// # Ok(()) + /// # } + /// # fn main() { + /// # try_main().unwrap(); + /// # } + /// ``` + fn from_str(key: &str) -> Result { + let k = decode_str_32(key)?; + Ok(Self::from_hacl_secret_key(curve25519::SecretKey(k))) + } +} + +#[derive(Copy, Clone)] +pub struct PublicKey { + k: [u8; DHLEN], +} +impl PublicKey { + /// Instanciates a new empty `PublicKey`. + pub fn empty() -> Self { + Self { + k: EMPTY_KEY, + } + } + /// Instanciates a new `PublicKey` from an array of `DHLEN` bytes. + pub fn from_bytes(k: [u8; DHLEN]) -> Result { + for i in forbidden_curve_values.iter() { + if &k == i { + return Err(NoiseError::InvalidPublicKeyError); + } + } + Ok(Self { + k, + }) + } + + pub(crate) fn clear(&mut self) { + self.k.zeroize(); + } + pub(crate) fn from_hacl_public_key(hacl_public: curve25519::PublicKey) -> Self { + Self { + k: hacl_public.0, + } + } + pub fn as_bytes(&self) -> [u8; DHLEN] { + self.k + } + /// Checks whether a `PublicKey` object is empty or not. + /// # Example + /// + /// ``` + /// # use noiseexplorer_nkpsk0::{ + /// # error::NoiseError, + /// # types::PublicKey, + /// # }; + /// # use std::str::FromStr; + /// # fn try_main() -> Result<(), NoiseError> { + /// let empty_key = PublicKey::empty(); + /// let k = PublicKey::from_str("4a3acbfdb163dec651dfa3194dece676d437029c62a408b4c5ea9114246e4893")?; + /// assert!(empty_key.is_empty()); + /// assert!(!k.is_empty()); + /// # Ok(()) + /// # } + /// # fn main() { + /// # try_main().unwrap(); + /// # } + /// ``` + pub fn is_empty(&self) -> bool { + constant_time_eq(&self.k[..], &EMPTY_KEY) + } +} + impl std::str::FromStr for PublicKey { + type Err = NoiseError; + /// Instanciates a new `PublicKey` from a string of hexadecimal values. + /// Returns `Ok(PublicKey)` when successful and `Err(NoiseError)` otherwise. + /// # Example + /// + /// ``` + /// # use noiseexplorer_nkpsk0::{ + /// # error::NoiseError, + /// # types::PublicKey, + /// # }; + /// # use std::str::FromStr; + /// # fn try_main() -> Result<(), NoiseError> { + /// let k = PublicKey::from_str("4a3acbfdb163dec651dfa3194dece676d437029c62a408b4c5ea9114246e4893")?; + /// println!("{:?}", k.as_bytes()); + /// # Ok(()) + /// # } + /// # fn main() { + /// # try_main().unwrap(); + /// # } + /// ``` + fn from_str(key: &str) -> Result { + let pk = decode_str_32(key)?; + Ok(Self::from_hacl_public_key(curve25519::PublicKey(pk))) + } +} + +#[derive(Copy, Clone)] +pub(crate) struct Nonce { + n: u64, +} +impl Nonce { + pub(crate) fn new() -> Self { + Self { + n: 0_u64, + } + } + pub(crate) fn increment(&mut self) { + self.n += 1; + } + pub(crate) fn get_value(self) -> Result { + if self.n == MAX_NONCE { + return Err(NoiseError::ExhaustedNonceError); + } + Ok(self.n) + } +} + +pub struct Keypair { + private_key: PrivateKey, + public_key: PublicKey, +} +impl Keypair { + pub fn clear(&mut self) { + self.private_key.clear(); + self.public_key.clear(); + } + /// Instanciates a `Keypair` where the `PrivateKey` and `PublicKey` fields are filled with 0 bytes. + pub fn new_empty() -> Self { + Self { + private_key: PrivateKey::empty(), + public_key: PublicKey::empty(), + } + } + /// Instanciates a `Keypair` by generating a `PrivateKey` from random values using `thread_rng()`, then deriving the corresponding `PublicKey` + pub fn default() -> Self { + let hacl_keypair: (curve25519::SecretKey, curve25519::PublicKey) = + curve25519::keypair(rand::thread_rng()); + Self { + private_key: PrivateKey::from_hacl_secret_key(hacl_keypair.0), + public_key: PublicKey::from_hacl_public_key(hacl_keypair.1), + } + } + pub(crate) fn dh(&self, public_key: &[u8; DHLEN]) -> [u8; DHLEN] { + let mut output: [u8; DHLEN] = EMPTY_KEY; + curve25519::scalarmult(&mut output, &self.private_key.as_bytes(), public_key); + output + } + /// Checks if the `PrivateKey` field of a `Keypair` is empty and returns either `true` or `false` accordingly. + pub fn is_empty(&self) -> bool { + self.private_key.is_empty() + } + /// Derives a `PublicKey` from a `Key` object. + /// Returns a `Ok(Keypair)` containing the previous two values and `Err(NoiseError)` otherwise. + pub fn from_key(k: PrivateKey) -> Result { + let public_key: PublicKey = k.generate_public_key()?; + Ok(Self { + private_key: k, + public_key, + }) + } + /// Derives a `PublicKey` from a `PrivateKey`. + /// Returns a `Ok(Keypair)` containing the previous two values and `Err(NoiseError)` otherwise. + pub fn from_private_key(k: PrivateKey) -> Result { + Self::from_key(k) + } + /// Returns the `PublicKey` value from the `Keypair` + pub fn get_public_key(&self) -> PublicKey { + self.public_key + } +} + + #[test] +fn public_key_validation_test() { + let bad_public_keys = vec![ + PublicKey::from_bytes([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), + PublicKey::from_bytes([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), + PublicKey::from_bytes([224, 235, 122, 124, 59, 65, 184, 174, 22, 86, 227, 250, 241, 159, 196, 106, 218, 9, 141, 235, 156, 50, 177, 253, 134, 98, 5, 22, 95, 73, 184, 0]), + PublicKey::from_bytes([95, 156, 149, 188, 163, 80, 140, 36, 177, 208, 177, 85, 156, 131, 239, 91, 4, 68, 92, 196, 88, 28, 142, 134, 216, 34, 78, 221, 208, 159, 17, 87]), + PublicKey::from_bytes([236, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127]), + PublicKey::from_bytes([237, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127]), + PublicKey::from_bytes([238, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127]), + PublicKey::from_bytes([205, 235, 122, 124, 59, 65, 184, 174, 22, 86, 227, 250, 241, 159, 196, 106, 218, 9, 141, 235, 156, 50, 177, 253, 134, 98, 5, 22, 95, 73, 184, 128]), + PublicKey::from_bytes([76, 156, 149, 188, 163, 80, 140, 36, 177, 208, 177, 85, 156, 131, 239, 91, 4, 68, 92, 196, 88, 28, 142, 134, 216, 34, 78, 221, 208, 159, 17, 215]), + PublicKey::from_bytes([217, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255]), + PublicKey::from_bytes([218, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255]), + PublicKey::from_bytes([219, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255]), + ]; + + for i in bad_public_keys { + if i.is_ok() { + panic!("Allowed forbidden Public Key value"); + } + } + + if PublicKey::from_bytes(decode_str_32("31e0303fd6418d2f8c0e78b91f22e8caed0fbe48656dcf4767e4834f701b8f62").unwrap()).is_err() { + panic!("Denied valid Public Key value"); + } +} \ No newline at end of file diff --git a/nkpsk0/src/utils.rs b/nkpsk0/src/utils.rs new file mode 100644 index 000000000..e5c684501 --- /dev/null +++ b/nkpsk0/src/utils.rs @@ -0,0 +1,16 @@ +/* ---------------------------------------------------------------- * + * UTILITY FUNCTIONS * + * ---------------------------------------------------------------- */ +use crate::consts::{EMPTY_HASH, HASHLEN, NONCE_LENGTH}; + +pub(crate) fn from_slice_hashlen(bytes: &[u8]) -> [u8; HASHLEN] { + let mut array = EMPTY_HASH; + let bytes = &bytes[..array.len()]; + array.copy_from_slice(bytes); + array +} +pub(crate) fn prep_nonce(n: u64) -> [u8;12] { + let mut nonce: [u8; NONCE_LENGTH] = [0_u8; NONCE_LENGTH]; + nonce[4..].copy_from_slice(&n.to_le_bytes()); + nonce +} diff --git a/payjoin-client/Cargo.toml b/payjoin-client/Cargo.toml index 2ad0d9325..ebf608974 100644 --- a/payjoin-client/Cargo.toml +++ b/payjoin-client/Cargo.toml @@ -15,3 +15,4 @@ clap = "2.33.3" turn = "0.6.1" tokio = { version = "1.6.1", features = ["full"] } webrtc-util = { version = "0.7.0", default-features = false, features = ["conn", "vnet"] } +nkpsk0 = { path = "../nkpsk0" } \ No newline at end of file diff --git a/payjoin-client/src/main.rs b/payjoin-client/src/main.rs index 54cebf250..61f676280 100644 --- a/payjoin-client/src/main.rs +++ b/payjoin-client/src/main.rs @@ -1,8 +1,14 @@ use std::collections::HashMap; use std::convert::TryFrom; +use std::convert::TryInto; use std::str::FromStr; use std::sync::Arc; +use nkpsk0::consts::DHLEN; +use nkpsk0::consts::MAC_LENGTH; +use nkpsk0::types::Keypair; +use nkpsk0::types::Psk; +use nkpsk0::types::PublicKey; use turn::client::*; use turn::Error; use bitcoincore_rpc::bitcoin::Amount; @@ -14,6 +20,8 @@ use tokio::net::UdpSocket; use tokio::time::Duration; use webrtc_util::Conn; +use nkpsk0::noisesession::NoiseSession; + #[tokio::main] async fn main() -> Result<(), Error> { let mut app = App::new("payjoin-client") @@ -39,14 +47,17 @@ async fn main() -> Result<(), Error> { .short("b") .long("bip21") .help("The BIP21 URI to send to") - .takes_value(true) - .group("send")) + .takes_value(true)) .arg(Arg::with_name("endpoint") - .short("e") - .long("endpoint") - .help("The endpoint to send the payjoin to") - .takes_value(true) - .group("send")) + .short("e") + .long("endpoint") + .help("The endpoint to send the payjoin to") + .takes_value(true)) + .arg(Arg::with_name("rs") + .short("s") + .long("rs") + .help("The receiver's static public key") + .takes_value(true)) .arg(Arg::with_name("relay") .short("r") @@ -81,7 +92,8 @@ async fn main() -> Result<(), Error> { } else { //let bip21 = matches.value_of("bip21").unwrap().as_ref(); let endpoint = matches.value_of("endpoint").unwrap(); - do_send(endpoint).await?; + let rs = matches.value_of("rs").unwrap(); + do_send(endpoint, rs).await?; //send_payjoin(bip21, bitcoind); } @@ -118,29 +130,34 @@ async fn listen_receiver(relay: &str) -> Result<(), Error> { // socket. let relay_conn = client.allocate().await?; + let static_key = Keypair::default(); + let s_base64 = base64::encode(static_key.get_public_key().as_bytes()); + let psk = Psk::default(); // could derive psk from bip21 address + let mut noise = NoiseSession::init_session(false, &[], static_key, None, psk); + // The relayConn's local address is actually the transport // address assigned on the TURN server. - println!("relayed-address={}", relay_conn.local_addr()?); + println!("--rs={} --endpoint={}", s_base64, relay_conn.local_addr()?); let mapped_addr = client.send_binding_request().await?; // punch UDP hole. after this packets from the IP address will be accepted by the turn server relay_conn.send_to("Hello".as_bytes(), mapped_addr).await?; // 2. Recv - listen_for_original_psbt(&client, relay_conn, mapped_addr).await?; + listen_for_original_psbt( relay_conn, &mut noise).await?; //receive_payjoin(relay, amount, bitcoind); client.close().await?; Ok(()) } async fn listen_for_original_psbt( - client: &Client, relay_conn: impl Conn + std::marker::Send + std::marker::Sync + 'static, - mapped_addr: std::net::SocketAddr, + noise: &mut NoiseSession, ) -> Result<(), Error> { let mut buf = [0u8; 1024]; let (n, from) = relay_conn.recv_from(&mut buf).await?; println!("received {} bytes from {}", n, from); + noise.recv_message(&mut buf[..n]).unwrap(); println!("received {}", String::from_utf8_lossy(&buf[..n])); relay_conn.send_to("PayJoin Proposal PSBT".as_bytes(), from).await?; @@ -149,15 +166,25 @@ async fn listen_for_original_psbt( Ok(()) } -async fn do_send(relay_addr: &str) -> Result<(), Error> { +async fn do_send(relay_addr: &str, rs_base64: &str) -> Result<(), Error> { + let msg = "Original PSBT".to_owned(); //format!("{:?}", tokio::time::Instant::now()); + println!("sending msg={} with size={}", msg, msg.as_bytes().len()); + + let s = Keypair::default(); + let rs_bytes: [u8; DHLEN] = base64::decode(rs_base64).unwrap().try_into().unwrap(); + let rs = PublicKey::from_bytes(rs_bytes).unwrap(); + let psk = Psk::default(); // could use bitcoin address as psk, ⚠️ empty for demo + let mut noise = NoiseSession::init_session(true, &[], s, Some(rs), psk); + noise.set_ephemeral_keypair(Keypair::default()); // Set up pinger socket (pingerConn) //println!("bind..."); let pinger_conn_tx = Arc::new(UdpSocket::bind("0.0.0.0:0").await?); let pinger_conn_rx = Arc::clone(&pinger_conn_tx); + let mut msg = msg.into_bytes(); + msg.resize(msg.len() + DHLEN + MAC_LENGTH, 0); // + buf + MAC_LENGTH + DH_LENGTH + noise.send_message(&mut msg).unwrap(); - let msg = "Original PSBT".to_owned(); //format!("{:?}", tokio::time::Instant::now()); - println!("sending msg={} with size={}", msg, msg.as_bytes().len()); - pinger_conn_tx.send_to(msg.as_bytes(), relay_addr).await?; + pinger_conn_tx.send_to(msg.as_slice(), relay_addr).await?; let mut buf = [0u8; 1024]; diff --git a/payjoin-relay/Cargo.toml b/payjoin-relay/Cargo.toml index 42987f992..a0976bfa8 100644 --- a/payjoin-relay/Cargo.toml +++ b/payjoin-relay/Cargo.toml @@ -16,4 +16,4 @@ webrtc-util = { version = "0.7.0", default-features = false, features = ["conn", clap = "2.33.3" tokio = { version = "1.6.1", features = ["full"] } log = "0.4.14" -env_logger = "0.9.0" \ No newline at end of file +env_logger = "0.9.0" From 06fcb25e0fb2a1671cf4fc3006a71ab4ed9ee734 Mon Sep 17 00:00:00 2001 From: DanGould Date: Sat, 14 Jan 2023 15:34:28 -0500 Subject: [PATCH 10/24] Rm do_ping_test() --- payjoin-client/src/main.rs | 83 -------------------------------------- 1 file changed, 83 deletions(-) diff --git a/payjoin-client/src/main.rs b/payjoin-client/src/main.rs index 61f676280..c2a423810 100644 --- a/payjoin-client/src/main.rs +++ b/payjoin-client/src/main.rs @@ -285,86 +285,3 @@ fn serialize_psbt(psbt: &Psbt) -> String { ) .unwrap() } - -// ** - -async fn do_ping_test( - client: &Client, - relay_conn: impl Conn + std::marker::Send + std::marker::Sync + 'static, -) -> Result<(), Error> { - // Send BindingRequest to learn our external IP - let mapped_addr = client.send_binding_request().await?; - - // Set up pinger socket (pingerConn) - //println!("bind..."); - let pinger_conn_tx = Arc::new(UdpSocket::bind("0.0.0.0:0").await?); - - // Punch a UDP hole for the relay_conn by sending a data to the mapped_addr. - // This will trigger a TURN client to generate a permission request to the - // TURN server. After this, packets from the IP address will be accepted by - // the TURN server. - //println!("relay_conn send hello to mapped_addr {}", mapped_addr); - relay_conn.send_to("\"Original PSBT\"".as_bytes(), mapped_addr).await?; - let relay_addr = relay_conn.local_addr()?; - - let pinger_conn_rx = Arc::clone(&pinger_conn_tx); - - // Start read-loop on pingerConn - tokio::spawn(async move { - let mut buf = vec![0u8; 1500]; - loop { - let (n, from) = match pinger_conn_rx.recv_from(&mut buf).await { - Ok((n, from)) => (n, from), - Err(_) => break, - }; - - let msg = match String::from_utf8(buf[..n].to_vec()) { - Ok(msg) => msg, - Err(_) => break, - }; - - println!("pingerConn read-loop: {} from {}", msg, from); - /*if sentAt, pingerErr := time.Parse(time.RFC3339Nano, msg); pingerErr == nil { - rtt := time.Since(sentAt) - log.Printf("%d bytes from from %s time=%d ms\n", n, from.String(), int(rtt.Seconds()*1000)) - }*/ - } - }); - - // Start read-loop on relay_conn - tokio::spawn(async move { - let mut buf = vec![0u8; 1500]; - loop { - let (n, from) = match relay_conn.recv_from(&mut buf).await { - Err(_) => break, - Ok((n, from)) => (n, from), - }; - - println!("relay_conn read-loop: {:?} from {}", &buf[..n], from); - - // Echo back - if relay_conn.send_to(&buf[..n], from).await.is_err() { - break; - } - } - }); - - tokio::time::sleep(Duration::from_millis(500)).await; - - /*println!( - "pinger_conn_tx send 10 packets to relay addr {}...", - relay_addr - );*/ - // Send 10 packets from relay_conn to the echo server - for _ in 0..2 { - let msg = "12345678910".to_owned(); //format!("{:?}", tokio::time::Instant::now()); - println!("sending msg={} with size={}", msg, msg.as_bytes().len()); - pinger_conn_tx.send_to(msg.as_bytes(), relay_addr).await?; - - // For simplicity, this example does not wait for the pong (reply). - // Instead, sleep 1 second. - tokio::time::sleep(Duration::from_secs(1)).await; - } - - Ok(()) -} \ No newline at end of file From b2b410101ec66d7b210ee367c549bc0730ec11af Mon Sep 17 00:00:00 2001 From: DanGould Date: Sun, 15 Jan 2023 11:52:52 -0500 Subject: [PATCH 11/24] Do receive logic --- payjoin-client/src/main.rs | 107 ++++++++++++++++++++++++++++++------- 1 file changed, 89 insertions(+), 18 deletions(-) diff --git a/payjoin-client/src/main.rs b/payjoin-client/src/main.rs index c2a423810..67ceb9983 100644 --- a/payjoin-client/src/main.rs +++ b/payjoin-client/src/main.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use std::convert::TryFrom; use std::convert::TryInto; +use std::net::SocketAddr; use std::str::FromStr; use std::sync::Arc; @@ -11,6 +12,7 @@ use nkpsk0::types::Psk; use nkpsk0::types::PublicKey; use turn::client::*; use turn::Error; +use bitcoincore_rpc::bitcoin::Address; use bitcoincore_rpc::bitcoin::Amount; use bitcoincore_rpc::RpcApi; use clap::{App, AppSettings, Arg}; @@ -88,7 +90,9 @@ async fn main() -> Result<(), Error> { if matches.is_present("relay") { let relay = matches.value_of("relay").unwrap(); - listen_receiver(relay).await?; + let amount = matches.value_of("amount").unwrap(); + let amount = Amount::from_sat(amount.parse::().unwrap()); + listen_receiver(relay, amount, bitcoind).await?; } else { //let bip21 = matches.value_of("bip21").unwrap().as_ref(); let endpoint = matches.value_of("endpoint").unwrap(); @@ -100,7 +104,7 @@ async fn main() -> Result<(), Error> { Ok(()) } -async fn listen_receiver(relay: &str) -> Result<(), Error> { +async fn listen_receiver(relay: &str, amount: Amount, bitcoind: bitcoincore_rpc::Client) -> Result<(), Error> { // 1. ConnectReceive // Ensure relay connection // TURN client won't create a local listening socket by itself. @@ -137,30 +141,81 @@ async fn listen_receiver(relay: &str) -> Result<(), Error> { // The relayConn's local address is actually the transport // address assigned on the TURN server. - println!("--rs={} --endpoint={}", s_base64, relay_conn.local_addr()?); + let relay_conn_endpoint = relay_conn.local_addr()?; + println!("--rs={} --endpoint={}", s_base64, relay_conn_endpoint); let mapped_addr = client.send_binding_request().await?; // punch UDP hole. after this packets from the IP address will be accepted by the turn server relay_conn.send_to("Hello".as_bytes(), mapped_addr).await?; // 2. Recv - listen_for_original_psbt( relay_conn, &mut noise).await?; - //receive_payjoin(relay, amount, bitcoind); + let pj_receiver_address = bitcoind.get_new_address(None, None).unwrap(); + print_payjoin_uri(pj_receiver_address, amount, relay_conn_endpoint); + process_original_psbt(relay_conn, &mut noise, bitcoind).await?; client.close().await?; Ok(()) } -async fn listen_for_original_psbt( + +async fn process_original_psbt( relay_conn: impl Conn + std::marker::Send + std::marker::Sync + 'static, noise: &mut NoiseSession, + receiver: bitcoincore_rpc::Client, ) -> Result<(), Error> { + use payjoin::bitcoin; + use payjoin::receiver::UncheckedProposal; + use payjoin::bitcoin::consensus; + use payjoin::bitcoin::blockdata::script::Script; + use payjoin::bitcoin::blockdata::transaction::TxOut; + let mut buf = [0u8; 1024]; let (n, from) = relay_conn.recv_from(&mut buf).await?; - println!("received {} bytes from {}", n, from); + println!("received {} bytes of supposed Original PSBT from {}", n, from); noise.recv_message(&mut buf[..n]).unwrap(); - println!("received {}", String::from_utf8_lossy(&buf[..n])); - relay_conn.send_to("PayJoin Proposal PSBT".as_bytes(), from).await?; + // query, headers not passed by default udp + // We'll need to figure out how to pass query info at least. + let query = ""; + let headers = MockHeaders::new(n); + let proposal = UncheckedProposal::from_request(&buf[..n], query, headers).unwrap(); + let checked_proposal = proposal.assume_tested_and_scheduled_broadcast().assume_inputs_not_owned().assume_no_mixed_input_scripts().assume_no_inputs_seen_before(); + + let mut original_psbt = checked_proposal.psbt(); + original_psbt.unsigned_tx.input.iter_mut().for_each(|txin| txin.script_sig = Script::default()); + + let mut payjoin_proposal_psbt = original_psbt.clone(); + let receiver_vout = 0; // correct??? + // TODO add selected receiver input utxo + // substitute receiver output + let receiver_substitute_address = receiver.get_new_address(None, None).unwrap(); + let receiver_substitute_address = bitcoin::Address::from_str(&receiver_substitute_address.to_string()).unwrap(); + let substitute_txout = TxOut { + value: payjoin_proposal_psbt.unsigned_tx.output[receiver_vout].value, + script_pubkey: receiver_substitute_address.script_pubkey(), + }; + payjoin_proposal_psbt.unsigned_tx.output[receiver_vout] = substitute_txout; + payjoin_proposal_psbt + .outputs + .resize_with(payjoin_proposal_psbt.unsigned_tx.output.len(), Default::default); + + // Sign payjoin psbt + let payjoin_base64_string = base64::encode(consensus::serialize(&payjoin_proposal_psbt)); + let payjoin_proposal_psbt = + receiver.wallet_process_psbt(&payjoin_base64_string, None, None, None).unwrap().psbt; + let payjoin_proposal_psbt = + receiver.finalize_psbt(&payjoin_proposal_psbt, Some(false)).unwrap().psbt.unwrap(); + let payjoin_proposal_psbt = + load_psbt_from_base64(payjoin_proposal_psbt.as_bytes()).unwrap(); + println!("Receiver's PayJoin proposal PSBT: {:#?}", payjoin_proposal_psbt); + + // Remove vestigial invalid signature data from the Original PSBT + let payjoin_proposal_psbt = + Psbt::from_unsigned_tx(payjoin_proposal_psbt.unsigned_tx.clone()) + .expect("resetting tx failed"); + let mut payjoin_proposal_psbt = consensus::serialize(&payjoin_proposal_psbt); + payjoin_proposal_psbt.resize(payjoin_proposal_psbt.len() + DHLEN + MAC_LENGTH, 0u8); + noise.send_message(&mut payjoin_proposal_psbt).unwrap(); + relay_conn.send_to(payjoin_proposal_psbt.as_slice(), from).await?; println!("sent PayJin Proposal"); Ok(()) @@ -194,19 +249,17 @@ async fn do_send(relay_addr: &str, rs_base64: &str) -> Result<(), Error> { Ok(()) } -fn receive_payjoin(relay: &str, amount: &str, client: bitcoincore_rpc::Client) { - // register the payjoin endpoint with an allocation at the TURN relay +fn print_payjoin_uri<'a>(address: Address, amount: Amount, endpoint: SocketAddr) { // Receiver creates the bip21 payjoin URI - let pj_receiver_address = client.get_new_address(None, None).unwrap(); - let amount = Amount::from_str(amount).unwrap(); let pj_uri_string = format!( - "{}?amount={}&pj={}&s=secret", - pj_receiver_address.to_qr_uri(), + "{}?amount={}&pj={}", + address.to_qr_uri(), amount.as_btc(), - relay, + format!("udp://{}", endpoint) ); - let pj_uri = payjoin::Uri::from_str(&pj_uri_string).unwrap(); - let _pj_uri = pj_uri.check_pj_supported().expect("Bad Uri"); + println!("PayJoin URI: {}", pj_uri_string); + let _pj_uri = payjoin::Uri::from_str(&pj_uri_string).unwrap(); // just validate the Uri + // pj_uri.check_pj_supported().expect("Bad Uri"); ignore this because we are using udp not https and endpoint will fail } fn send_payjoin<'a>(bip21: &str, client: bitcoincore_rpc::Client) -> bitcoincore_rpc::bitcoin::Txid { @@ -285,3 +338,21 @@ fn serialize_psbt(psbt: &Psbt) -> String { ) .unwrap() } + +pub struct MockHeaders { + length: String, +} + +impl MockHeaders { + fn new(length: usize) -> MockHeaders { MockHeaders { length: length.to_string() } } +} + +impl payjoin::receiver::Headers for MockHeaders { + fn get_header(&self, key: &str) -> Option<&str> { + match key { + "content-length" => Some(&self.length), + "content-type" => Some("text/plain"), + _ => None, + } + } +} \ No newline at end of file From 49e34b75dbb6ca5f9eb018ee4fe343ab35cb6624 Mon Sep 17 00:00:00 2001 From: DanGould Date: Sun, 15 Jan 2023 18:36:41 -0500 Subject: [PATCH 12/24] BROKEN do payjoin --- payjoin-client/src/main.rs | 97 +++++++++++++++++++++++++++++--------- 1 file changed, 74 insertions(+), 23 deletions(-) diff --git a/payjoin-client/src/main.rs b/payjoin-client/src/main.rs index 67ceb9983..54291465a 100644 --- a/payjoin-client/src/main.rs +++ b/payjoin-client/src/main.rs @@ -5,6 +5,7 @@ use std::net::SocketAddr; use std::str::FromStr; use std::sync::Arc; +use bitcoincore_rpc::bitcoin::util::psbt::PartiallySignedTransaction; use nkpsk0::consts::DHLEN; use nkpsk0::consts::MAC_LENGTH; use nkpsk0::types::Keypair; @@ -94,11 +95,15 @@ async fn main() -> Result<(), Error> { let amount = Amount::from_sat(amount.parse::().unwrap()); listen_receiver(relay, amount, bitcoind).await?; } else { - //let bip21 = matches.value_of("bip21").unwrap().as_ref(); + let bip21 = matches.value_of("bip21").unwrap().as_ref(); let endpoint = matches.value_of("endpoint").unwrap(); let rs = matches.value_of("rs").unwrap(); - do_send(endpoint, rs).await?; - //send_payjoin(bip21, bitcoind); + let (req, ctx) = create_pj_request(bip21, &bitcoind); //base64 request + let payjoin_psbt = do_send(req, ctx, endpoint, rs).await?; + let psbt = bitcoind.wallet_process_psbt(&serialize_psbt(&payjoin_psbt), None, None, None).unwrap().psbt; + let tx = bitcoind.finalize_psbt(&psbt, Some(true)).unwrap().hex.expect("incomplete psbt"); + let txid = bitcoind.send_raw_transaction(&tx).unwrap(); + println!("Sent tx: {}", txid); } Ok(()) @@ -142,7 +147,7 @@ async fn listen_receiver(relay: &str, amount: Amount, bitcoind: bitcoincore_rpc: // The relayConn's local address is actually the transport // address assigned on the TURN server. let relay_conn_endpoint = relay_conn.local_addr()?; - println!("--rs={} --endpoint={}", s_base64, relay_conn_endpoint); + print!("--rs={} --endpoint={} ", s_base64, relay_conn_endpoint); let mapped_addr = client.send_binding_request().await?; // punch UDP hole. after this packets from the IP address will be accepted by the turn server @@ -150,7 +155,7 @@ async fn listen_receiver(relay: &str, amount: Amount, bitcoind: bitcoincore_rpc: // 2. Recv let pj_receiver_address = bitcoind.get_new_address(None, None).unwrap(); - print_payjoin_uri(pj_receiver_address, amount, relay_conn_endpoint); + print_payjoin_uri(pj_receiver_address, amount); process_original_psbt(relay_conn, &mut noise, bitcoind).await?; client.close().await?; @@ -221,9 +226,8 @@ async fn process_original_psbt( Ok(()) } -async fn do_send(relay_addr: &str, rs_base64: &str) -> Result<(), Error> { - let msg = "Original PSBT".to_owned(); //format!("{:?}", tokio::time::Instant::now()); - println!("sending msg={} with size={}", msg, msg.as_bytes().len()); +async fn do_send(req: payjoin::sender::Request, ctx: payjoin::sender::Context, relay_addr: &str, rs_base64: &str) -> Result { + let mut msg = req.body; //format!("{:?}", tokio::time::Instant::now()); let s = Keypair::default(); let rs_bytes: [u8; DHLEN] = base64::decode(rs_base64).unwrap().try_into().unwrap(); @@ -232,47 +236,91 @@ async fn do_send(relay_addr: &str, rs_base64: &str) -> Result<(), Error> { let mut noise = NoiseSession::init_session(true, &[], s, Some(rs), psk); noise.set_ephemeral_keypair(Keypair::default()); // Set up pinger socket (pingerConn) - //println!("bind..."); + // println!("bind..."); let pinger_conn_tx = Arc::new(UdpSocket::bind("0.0.0.0:0").await?); let pinger_conn_rx = Arc::clone(&pinger_conn_tx); - let mut msg = msg.into_bytes(); msg.resize(msg.len() + DHLEN + MAC_LENGTH, 0); // + buf + MAC_LENGTH + DH_LENGTH noise.send_message(&mut msg).unwrap(); - pinger_conn_tx.send_to(msg.as_slice(), relay_addr).await?; - + + println!("len: {} < 1024?", msg.len()); let mut buf = [0u8; 1024]; let (n, from) = pinger_conn_rx.recv_from(&mut buf).await?; - let msg = String::from_utf8(buf[..n].to_vec()).unwrap(); - println!("response: {} from {}", msg, from); - Ok(()) + noise.recv_message(&mut buf[..n]).unwrap(); + let proposal = ctx.process_response(&buf[..n]).unwrap(); + println!("proposal: {:#?} from {}", proposal, from); + Ok(proposal) } -fn print_payjoin_uri<'a>(address: Address, amount: Amount, endpoint: SocketAddr) { +// get a valid BIP 21 URI for payjoin as defined in BIP 78 +// we have to use a mock endpoing because we are using udp not https +fn print_payjoin_uri<'a>(address: Address, amount: Amount) { // Receiver creates the bip21 payjoin URI + let mock_endpoint = "https://example.com"; let pj_uri_string = format!( "{}?amount={}&pj={}", address.to_qr_uri(), amount.as_btc(), - format!("udp://{}", endpoint) + mock_endpoint, ); - println!("PayJoin URI: {}", pj_uri_string); + println!("--bip21=\"{}\"", pj_uri_string); let _pj_uri = payjoin::Uri::from_str(&pj_uri_string).unwrap(); // just validate the Uri // pj_uri.check_pj_supported().expect("Bad Uri"); ignore this because we are using udp not https and endpoint will fail } -fn send_payjoin<'a>(bip21: &str, client: bitcoincore_rpc::Client) -> bitcoincore_rpc::bitcoin::Txid { +fn create_pj_request(bip21: &str, client: &bitcoincore_rpc::Client) -> (payjoin::sender::Request, payjoin::sender::Context) { let link = payjoin::Uri::try_from(&*bip21).unwrap(); + if link.amount.is_none() { + panic!("please specify the amount in the Uri"); + } + + // ⚠️ we're hacking around this check by using a dummy endpoint. This is not a good idea in production + // We use a udp endpoint that would not support BIP 78 payjoin instead. let link = link .check_pj_supported() .unwrap_or_else(|_| panic!("The provided URI doesn't support payjoin (BIP78)")); + let amount = Amount::from_sat(link.amount.unwrap().to_sat()); + let mut outputs = HashMap::with_capacity(1); + outputs.insert(link.address.to_string(), amount); + + let options = bitcoincore_rpc::json::WalletCreateFundedPsbtOptions { + lock_unspent: Some(true), + fee_rate: Some(Amount::from_sat(2000)), + ..Default::default() + }; + let psbt = client + .wallet_create_funded_psbt( + &[], // inputs + &outputs, + None, // locktime + Some(options), + None, + ) + .expect("failed to create PSBT") + .psbt; + let psbt = client.wallet_process_psbt(&psbt, None, None, None).unwrap().psbt; + let psbt = load_psbt_from_base64(psbt.as_bytes()).unwrap(); + println!("Original psbt: {:#?}", psbt); + let pj_params = payjoin::sender::Configuration::non_incentivizing(); + link.create_pj_request(psbt, pj_params).unwrap() +} + +fn send_payjoin<'a>(bip21: &str, client: bitcoincore_rpc::Client) -> bitcoincore_rpc::bitcoin::Txid { + let link = payjoin::Uri::try_from(&*bip21).unwrap(); + // ⚠️ we're hacking around this check by using a dummy endpoint. This is not a good idea in production + // We use a udp endpoint that would not support BIP 78 payjoin instead. if link.amount.is_none() { panic!("please specify the amount in the Uri"); } + // this check won't work because PayJoin v2 here uses udp not https or .onion + let link = link + .check_pj_supported() + .unwrap_or_else(|_| panic!("The provided URI doesn't support payjoin (BIP78)")); + let amount = Amount::from_sat(link.amount.unwrap().to_sat()); let mut outputs = HashMap::with_capacity(1); outputs.insert(link.address.to_string(), amount); @@ -295,11 +343,10 @@ fn send_payjoin<'a>(bip21: &str, client: bitcoincore_rpc::Client) -> bitcoincore let psbt = client.wallet_process_psbt(&psbt, None, None, None).unwrap().psbt; let psbt = load_psbt_from_base64(psbt.as_bytes()).unwrap(); println!("Original psbt: {:#?}", psbt); - let pj_params = payjoin::sender::Configuration::with_fee_contribution( - payjoin::bitcoin::Amount::from_sat(10000), - None, - ); + let pj_params = payjoin::sender::Configuration::non_incentivizing(); let (req, ctx) = link.create_pj_request(psbt, pj_params).unwrap(); + + // *** send, receive using noise + TURN let response = reqwest::blocking::Client::new() .post(req.url) .body(req.body) @@ -308,6 +355,10 @@ fn send_payjoin<'a>(bip21: &str, client: bitcoincore_rpc::Client) -> bitcoincore .expect("failed to communicate"); //.error_for_status() //.unwrap(); + + // *** end send, receive using noise + TURN + + let psbt = ctx.process_response(response).unwrap(); println!("Proposed psbt: {:#?}", psbt); let psbt = client.wallet_process_psbt(&serialize_psbt(&psbt), None, None, None).unwrap().psbt; From a77ea232da06d183ff73143f64fc1489770eb10c Mon Sep 17 00:00:00 2001 From: DanGould Date: Sat, 21 Jan 2023 10:24:50 -0500 Subject: [PATCH 13/24] =?UTF-8?q?Send=20PayJoin=20over=20relay=20=E2=9C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- payjoin-client/src/main.rs | 112 ++++++++----------------------------- 1 file changed, 24 insertions(+), 88 deletions(-) diff --git a/payjoin-client/src/main.rs b/payjoin-client/src/main.rs index 54291465a..9657053a6 100644 --- a/payjoin-client/src/main.rs +++ b/payjoin-client/src/main.rs @@ -11,6 +11,7 @@ use nkpsk0::consts::MAC_LENGTH; use nkpsk0::types::Keypair; use nkpsk0::types::Psk; use nkpsk0::types::PublicKey; +use payjoin::bitcoin::hashes::hex::ToHex; use turn::client::*; use turn::Error; use bitcoincore_rpc::bitcoin::Address; @@ -97,9 +98,8 @@ async fn main() -> Result<(), Error> { } else { let bip21 = matches.value_of("bip21").unwrap().as_ref(); let endpoint = matches.value_of("endpoint").unwrap(); - let rs = matches.value_of("rs").unwrap(); let (req, ctx) = create_pj_request(bip21, &bitcoind); //base64 request - let payjoin_psbt = do_send(req, ctx, endpoint, rs).await?; + let payjoin_psbt = do_send(req, ctx, endpoint).await?; let psbt = bitcoind.wallet_process_psbt(&serialize_psbt(&payjoin_psbt), None, None, None).unwrap().psbt; let tx = bitcoind.finalize_psbt(&psbt, Some(true)).unwrap().hex.expect("incomplete psbt"); let txid = bitcoind.send_raw_transaction(&tx).unwrap(); @@ -139,24 +139,19 @@ async fn listen_receiver(relay: &str, amount: Amount, bitcoind: bitcoincore_rpc: // socket. let relay_conn = client.allocate().await?; - let static_key = Keypair::default(); - let s_base64 = base64::encode(static_key.get_public_key().as_bytes()); - let psk = Psk::default(); // could derive psk from bip21 address - let mut noise = NoiseSession::init_session(false, &[], static_key, None, psk); - // The relayConn's local address is actually the transport // address assigned on the TURN server. let relay_conn_endpoint = relay_conn.local_addr()?; - print!("--rs={} --endpoint={} ", s_base64, relay_conn_endpoint); + print!("--endpoint={} ", relay_conn_endpoint); let mapped_addr = client.send_binding_request().await?; // punch UDP hole. after this packets from the IP address will be accepted by the turn server relay_conn.send_to("Hello".as_bytes(), mapped_addr).await?; // 2. Recv - let pj_receiver_address = bitcoind.get_new_address(None, None).unwrap(); + let pj_receiver_address = bitcoind.get_new_address(None, Some(bitcoincore_rpc::json::AddressType::Bech32)).unwrap(); print_payjoin_uri(pj_receiver_address, amount); - process_original_psbt(relay_conn, &mut noise, bitcoind).await?; + process_original_psbt(relay_conn, bitcoind).await?; client.close().await?; Ok(()) @@ -164,7 +159,6 @@ async fn listen_receiver(relay: &str, amount: Amount, bitcoind: bitcoincore_rpc: async fn process_original_psbt( relay_conn: impl Conn + std::marker::Send + std::marker::Sync + 'static, - noise: &mut NoiseSession, receiver: bitcoincore_rpc::Client, ) -> Result<(), Error> { use payjoin::bitcoin; @@ -176,13 +170,24 @@ async fn process_original_psbt( let mut buf = [0u8; 1024]; let (n, from) = relay_conn.recv_from(&mut buf).await?; println!("received {} bytes of supposed Original PSBT from {}", n, from); - noise.recv_message(&mut buf[..n]).unwrap(); + // query, headers not passed by default udp // We'll need to figure out how to pass query info at least. let query = ""; let headers = MockHeaders::new(n); let proposal = UncheckedProposal::from_request(&buf[..n], query, headers).unwrap(); + + // Receive Check 1: Is Broadcastable + let original_tx = proposal.get_transaction_to_check_broadcast(); + let tx_is_broadcastable = receiver + .test_mempool_accept(&[bitcoin::consensus::encode::serialize(&original_tx).to_hex()]) + .unwrap() + .first() + .unwrap() + .allowed; + assert!(tx_is_broadcastable); + let checked_proposal = proposal.assume_tested_and_scheduled_broadcast().assume_inputs_not_owned().assume_no_mixed_input_scripts().assume_no_inputs_seen_before(); let mut original_psbt = checked_proposal.psbt(); @@ -192,7 +197,7 @@ async fn process_original_psbt( let receiver_vout = 0; // correct??? // TODO add selected receiver input utxo // substitute receiver output - let receiver_substitute_address = receiver.get_new_address(None, None).unwrap(); + let receiver_substitute_address = receiver.get_new_address(None, Some(bitcoincore_rpc::json::AddressType::Bech32)).unwrap(); let receiver_substitute_address = bitcoin::Address::from_str(&receiver_substitute_address.to_string()).unwrap(); let substitute_txout = TxOut { value: payjoin_proposal_psbt.unsigned_tx.output[receiver_vout].value, @@ -217,37 +222,26 @@ async fn process_original_psbt( let payjoin_proposal_psbt = Psbt::from_unsigned_tx(payjoin_proposal_psbt.unsigned_tx.clone()) .expect("resetting tx failed"); - let mut payjoin_proposal_psbt = consensus::serialize(&payjoin_proposal_psbt); - payjoin_proposal_psbt.resize(payjoin_proposal_psbt.len() + DHLEN + MAC_LENGTH, 0u8); - noise.send_message(&mut payjoin_proposal_psbt).unwrap(); - relay_conn.send_to(payjoin_proposal_psbt.as_slice(), from).await?; - println!("sent PayJin Proposal"); + let payjoin_proposal_psbt = base64::encode(consensus::serialize(&payjoin_proposal_psbt)); + //payjoin_proposal_psbt.resize(payjoin_proposal_psbt.len() + DHLEN + MAC_LENGTH, 0u8); + relay_conn.send_to(payjoin_proposal_psbt.as_bytes(), from).await?; + println!("sent PayJoin Proposal"); Ok(()) } -async fn do_send(req: payjoin::sender::Request, ctx: payjoin::sender::Context, relay_addr: &str, rs_base64: &str) -> Result { - let mut msg = req.body; //format!("{:?}", tokio::time::Instant::now()); +async fn do_send(req: payjoin::sender::Request, ctx: payjoin::sender::Context, relay_addr: &str) -> Result { + let msg = req.body; //format!("{:?}", tokio::time::Instant::now()); - let s = Keypair::default(); - let rs_bytes: [u8; DHLEN] = base64::decode(rs_base64).unwrap().try_into().unwrap(); - let rs = PublicKey::from_bytes(rs_bytes).unwrap(); - let psk = Psk::default(); // could use bitcoin address as psk, ⚠️ empty for demo - let mut noise = NoiseSession::init_session(true, &[], s, Some(rs), psk); - noise.set_ephemeral_keypair(Keypair::default()); // Set up pinger socket (pingerConn) - // println!("bind..."); let pinger_conn_tx = Arc::new(UdpSocket::bind("0.0.0.0:0").await?); let pinger_conn_rx = Arc::clone(&pinger_conn_tx); - msg.resize(msg.len() + DHLEN + MAC_LENGTH, 0); // + buf + MAC_LENGTH + DH_LENGTH - noise.send_message(&mut msg).unwrap(); pinger_conn_tx.send_to(msg.as_slice(), relay_addr).await?; println!("len: {} < 1024?", msg.len()); let mut buf = [0u8; 1024]; let (n, from) = pinger_conn_rx.recv_from(&mut buf).await?; - noise.recv_message(&mut buf[..n]).unwrap(); let proposal = ctx.process_response(&buf[..n]).unwrap(); println!("proposal: {:#?} from {}", proposal, from); Ok(proposal) @@ -308,64 +302,6 @@ fn create_pj_request(bip21: &str, client: &bitcoincore_rpc::Client) -> (payjoin: link.create_pj_request(psbt, pj_params).unwrap() } -fn send_payjoin<'a>(bip21: &str, client: bitcoincore_rpc::Client) -> bitcoincore_rpc::bitcoin::Txid { - let link = payjoin::Uri::try_from(&*bip21).unwrap(); - // ⚠️ we're hacking around this check by using a dummy endpoint. This is not a good idea in production - // We use a udp endpoint that would not support BIP 78 payjoin instead. - if link.amount.is_none() { - panic!("please specify the amount in the Uri"); - } - - // this check won't work because PayJoin v2 here uses udp not https or .onion - let link = link - .check_pj_supported() - .unwrap_or_else(|_| panic!("The provided URI doesn't support payjoin (BIP78)")); - - let amount = Amount::from_sat(link.amount.unwrap().to_sat()); - let mut outputs = HashMap::with_capacity(1); - outputs.insert(link.address.to_string(), amount); - - let options = bitcoincore_rpc::json::WalletCreateFundedPsbtOptions { - lock_unspent: Some(true), - fee_rate: Some(Amount::from_sat(2000)), - ..Default::default() - }; - let psbt = client - .wallet_create_funded_psbt( - &[], // inputs - &outputs, - None, // locktime - Some(options), - None, - ) - .expect("failed to create PSBT") - .psbt; - let psbt = client.wallet_process_psbt(&psbt, None, None, None).unwrap().psbt; - let psbt = load_psbt_from_base64(psbt.as_bytes()).unwrap(); - println!("Original psbt: {:#?}", psbt); - let pj_params = payjoin::sender::Configuration::non_incentivizing(); - let (req, ctx) = link.create_pj_request(psbt, pj_params).unwrap(); - - // *** send, receive using noise + TURN - let response = reqwest::blocking::Client::new() - .post(req.url) - .body(req.body) - .header("Content-Type", "text/plain") - .send() - .expect("failed to communicate"); - //.error_for_status() - //.unwrap(); - - // *** end send, receive using noise + TURN - - - let psbt = ctx.process_response(response).unwrap(); - println!("Proposed psbt: {:#?}", psbt); - let psbt = client.wallet_process_psbt(&serialize_psbt(&psbt), None, None, None).unwrap().psbt; - let tx = client.finalize_psbt(&psbt, Some(true)).unwrap().hex.expect("incomplete psbt"); - client.send_raw_transaction(&tx).unwrap() -} - fn load_psbt_from_base64( mut input: impl std::io::Read, ) -> Result { From 74a5147333e1482f51a75eaf9f4f206f325b007b Mon Sep 17 00:00:00 2001 From: DanGould Date: Sat, 21 Jan 2023 12:23:14 -0500 Subject: [PATCH 14/24] Init noise test --- nkpsk0/src/lib.rs | 51 +++++++++++++++++++++++++++++++++++++- nkpsk0/src/noisesession.rs | 11 -------- nkpsk0/src/state.rs | 2 -- 3 files changed, 50 insertions(+), 14 deletions(-) diff --git a/nkpsk0/src/lib.rs b/nkpsk0/src/lib.rs index ed1dc3aad..a07bce99b 100644 --- a/nkpsk0/src/lib.rs +++ b/nkpsk0/src/lib.rs @@ -22,4 +22,53 @@ pub(crate) mod utils; pub mod consts; pub mod error; pub mod noisesession; -pub mod types; \ No newline at end of file +pub mod types; + + +#[cfg(test)] +mod test { + use crate::consts::{MAC_LENGTH, DHLEN}; + use crate::noisesession::NoiseSession; + use crate::types::{Keypair, Psk, PublicKey}; + + #[test] + fn e2e_from_pj_sender() { + println!("test"); + + // <- s + let receiver_static = Keypair::default(); // s from responder aka payjoin receiver + let rs = receiver_static.get_public_key(); // rs from responder + + // ... + let original_psbt = b"Original PSBT"; + println!("original_psbt: {:?}", String::from_utf8(original_psbt.to_vec())); + println!("original_psbt bytes: {:?}", original_psbt); + // ... + + // Ready Message A -> psk, e, es + + // from the sender + let sender_static = Keypair::new_empty(); // the N in NK, should be nothing at all. + let psk = Psk::default(); // pre-shared (from WHO?) symmetric key. // TODO get from randomness, not default() emptyness + let mut sender = NoiseSession::init_session(true, b"prologue", sender_static, Some(rs), psk.clone()); + sender.set_ephemeral_keypair(Keypair::default()); // sender's e + + let mut in_out: Vec = Vec::new(); + in_out.extend(original_psbt); + let message_a_size = original_psbt.len() + DHLEN + MAC_LENGTH; + in_out.resize(message_a_size, 0); + sender.send_message(&mut in_out).unwrap(); // psk, e, es + let mut message_a = in_out; + + // from the receiver + let mut receiver = NoiseSession::init_session(false, b"prologue", receiver_static, None, psk); + receiver.set_ephemeral_keypair(Keypair::default()); // receiver's e + //let mut message_a_received: Vec = Vec::with_capacity(message_a_size); // you would have to make a sized buffer in implementation + receiver.recv_message(&mut message_a).unwrap(); // es derived internally + println!("message_a bytes:{:?}", message_a); + let orig = &message_a[..original_psbt.len()]; + println!("message_a decrypted:{:?}", String::from_utf8(orig.to_vec())); + + // Ready Message B <- e, ee + } +} \ No newline at end of file diff --git a/nkpsk0/src/noisesession.rs b/nkpsk0/src/noisesession.rs index d8a14fa03..07b427b2f 100644 --- a/nkpsk0/src/noisesession.rs +++ b/nkpsk0/src/noisesession.rs @@ -141,10 +141,8 @@ impl NoiseSession { if in_out.len() < MAC_LENGTH || in_out.len() > MAX_MESSAGE { return Err(NoiseError::UnsupportedMessageLengthError); } - println!("a"); if self.mc == 0 { self.hs.write_message_a(in_out)?; - println!("b"); } else if self.mc == 1 { @@ -154,15 +152,12 @@ impl NoiseSession { self.cs1 = temp.1; self.cs2 = temp.2; self.hs.clear(); - println!("c"); } else if self.i { self.cs1.write_message_regular(in_out)?; - println!("d"); } else { self.cs2.write_message_regular(in_out)?; - println!("e"); } self.mc += 1; @@ -182,10 +177,8 @@ impl NoiseSession { if in_out.len() < MAC_LENGTH || in_out.len() > MAX_MESSAGE { return Err(NoiseError::UnsupportedMessageLengthError); } - println!("a"); if self.mc == 0 { self.hs.read_message_a(in_out)?; - println!("b"); } else if self.mc == 1 { let temp = self.hs.read_message_b(in_out)?; @@ -194,16 +187,12 @@ impl NoiseSession { self.cs1 = temp.1; self.cs2 = temp.2; self.hs.clear(); - println!("c"); } else if self.i { self.cs2.read_message_regular(in_out)?; - println!("d"); } else { self.cs1.read_message_regular(in_out)?; - println!("e"); - } self.mc += 1; Ok(()) diff --git a/nkpsk0/src/state.rs b/nkpsk0/src/state.rs index 9258184a4..f467d415e 100644 --- a/nkpsk0/src/state.rs +++ b/nkpsk0/src/state.rs @@ -326,9 +326,7 @@ impl HandshakeState { self.ss.mix_hash(&self.re.as_bytes()[..DHLEN]); self.ss.mix_key(&self.re.as_bytes()); self.ss.mix_key(&self.s.dh(&self.re.as_bytes())); - println!("dec_&_hash"); self.ss.decrypt_and_hash(in_out)?; - println!("after dec_&_hash"); Ok(()) } From b2861fab07579e418f9d3e644e4b57592df214bf Mon Sep 17 00:00:00 2001 From: DanGould Date: Sat, 21 Jan 2023 13:59:45 -0500 Subject: [PATCH 15/24] Process NKpsk0 Message A --- nkpsk0/src/lib.rs | 20 ++++++++++++-------- nkpsk0/src/noisesession.rs | 1 - nkpsk0/src/state.rs | 4 ++++ 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/nkpsk0/src/lib.rs b/nkpsk0/src/lib.rs index a07bce99b..c803b82c1 100644 --- a/nkpsk0/src/lib.rs +++ b/nkpsk0/src/lib.rs @@ -50,24 +50,28 @@ mod test { // from the sender let sender_static = Keypair::new_empty(); // the N in NK, should be nothing at all. let psk = Psk::default(); // pre-shared (from WHO?) symmetric key. // TODO get from randomness, not default() emptyness - let mut sender = NoiseSession::init_session(true, b"prologue", sender_static, Some(rs), psk.clone()); - sender.set_ephemeral_keypair(Keypair::default()); // sender's e + let mut sender = NoiseSession::init_session(true, b"", sender_static, Some(rs), psk.clone()); - let mut in_out: Vec = Vec::new(); - in_out.extend(original_psbt); + let mut in_out: Vec = vec![0; DHLEN]; + println!("in_out bytes DHLEN: {:?}", in_out); + in_out.append(&mut original_psbt.to_vec()); + println!("in_out bytes DHLEN | msg: {:?}", in_out); let message_a_size = original_psbt.len() + DHLEN + MAC_LENGTH; in_out.resize(message_a_size, 0); + println!("in_out bytes DHLEN + msg + MAC_LENGTH:{:?}", in_out); + + println!("sender.is_transport() = {}", sender.is_transport()); sender.send_message(&mut in_out).unwrap(); // psk, e, es let mut message_a = in_out; // from the receiver - let mut receiver = NoiseSession::init_session(false, b"prologue", receiver_static, None, psk); - receiver.set_ephemeral_keypair(Keypair::default()); // receiver's e + let mut receiver = NoiseSession::init_session(false, b"", receiver_static, None, psk); //let mut message_a_received: Vec = Vec::with_capacity(message_a_size); // you would have to make a sized buffer in implementation receiver.recv_message(&mut message_a).unwrap(); // es derived internally println!("message_a bytes:{:?}", message_a); - let orig = &message_a[..original_psbt.len()]; - println!("message_a decrypted:{:?}", String::from_utf8(orig.to_vec())); + let (_re, payload) = message_a.split_at_mut(DHLEN); + let (payload, _mac) = payload.split_at_mut(payload.len() - MAC_LENGTH); + println!("message_a decrypted:{:?}", String::from_utf8(payload.to_vec())); // Ready Message B <- e, ee } diff --git a/nkpsk0/src/noisesession.rs b/nkpsk0/src/noisesession.rs index 07b427b2f..a386e844a 100644 --- a/nkpsk0/src/noisesession.rs +++ b/nkpsk0/src/noisesession.rs @@ -143,7 +143,6 @@ impl NoiseSession { } if self.mc == 0 { self.hs.write_message_a(in_out)?; - } else if self.mc == 1 { let temp = self.hs.write_message_b(in_out)?; diff --git a/nkpsk0/src/state.rs b/nkpsk0/src/state.rs index f467d415e..d667a56bf 100644 --- a/nkpsk0/src/state.rs +++ b/nkpsk0/src/state.rs @@ -198,6 +198,7 @@ impl SymmetricState { pub(crate) fn encrypt_and_hash(&mut self, in_out: &mut [u8]) -> Result<(), NoiseError> { let mut temp_mac: [u8; MAC_LENGTH] = [0u8;MAC_LENGTH]; let (plaintext, mac) = in_out.split_at_mut(in_out.len()-MAC_LENGTH); + println!("encrypt_and_hash: plaintext: {:?}", plaintext); self.cs.encrypt_with_ad(&self.h.as_bytes()[..], plaintext, &mut temp_mac)?; mac.copy_from_slice(&temp_mac[..]); self.mix_hash(in_out); @@ -286,8 +287,10 @@ impl HandshakeState { } if self.e.is_empty() { self.e = Keypair::default(); + println!("e is empty, generating new keypair"); } let (ne, in_out) = in_out.split_at_mut(DHLEN); + println!("in_out: {:?} this should be the payload + MAC", in_out); ne.copy_from_slice(&self.e.get_public_key().as_bytes()[..]); self.ss.mix_hash(ne); self.ss.mix_key(&self.e.get_public_key().as_bytes()); @@ -322,6 +325,7 @@ impl HandshakeState { return Err(NoiseError::MissingreError); } let (re, in_out) = in_out.split_at_mut(DHLEN); + println!("re: {:?}", re); // DH self.re = PublicKey::from_bytes(from_slice_hashlen(re))?; self.ss.mix_hash(&self.re.as_bytes()[..DHLEN]); self.ss.mix_key(&self.re.as_bytes()); From f65ad6db8d81e650fa4a46c25b5827f4869c4462 Mon Sep 17 00:00:00 2001 From: DanGould Date: Sat, 21 Jan 2023 14:20:22 -0500 Subject: [PATCH 16/24] Process NKpsk0 Message B --- nkpsk0/src/lib.rs | 43 +++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/nkpsk0/src/lib.rs b/nkpsk0/src/lib.rs index c803b82c1..5f70e878c 100644 --- a/nkpsk0/src/lib.rs +++ b/nkpsk0/src/lib.rs @@ -33,8 +33,6 @@ mod test { #[test] fn e2e_from_pj_sender() { - println!("test"); - // <- s let receiver_static = Keypair::default(); // s from responder aka payjoin receiver let rs = receiver_static.get_public_key(); // rs from responder @@ -42,37 +40,50 @@ mod test { // ... let original_psbt = b"Original PSBT"; println!("original_psbt: {:?}", String::from_utf8(original_psbt.to_vec())); - println!("original_psbt bytes: {:?}", original_psbt); // ... // Ready Message A -> psk, e, es - // from the sender + // from the initiator (payjoin sender) let sender_static = Keypair::new_empty(); // the N in NK, should be nothing at all. let psk = Psk::default(); // pre-shared (from WHO?) symmetric key. // TODO get from randomness, not default() emptyness - let mut sender = NoiseSession::init_session(true, b"", sender_static, Some(rs), psk.clone()); + let mut initiator = NoiseSession::init_session(true, b"", sender_static, Some(rs), psk.clone()); let mut in_out: Vec = vec![0; DHLEN]; - println!("in_out bytes DHLEN: {:?}", in_out); in_out.append(&mut original_psbt.to_vec()); - println!("in_out bytes DHLEN | msg: {:?}", in_out); - let message_a_size = original_psbt.len() + DHLEN + MAC_LENGTH; + let message_a_size = DHLEN + original_psbt.len() + MAC_LENGTH; in_out.resize(message_a_size, 0); - println!("in_out bytes DHLEN + msg + MAC_LENGTH:{:?}", in_out); - - println!("sender.is_transport() = {}", sender.is_transport()); - sender.send_message(&mut in_out).unwrap(); // psk, e, es + initiator.send_message(&mut in_out).unwrap(); // psk, e, es let mut message_a = in_out; - // from the receiver - let mut receiver = NoiseSession::init_session(false, b"", receiver_static, None, psk); + // from the responder (payjoin receiver) + let mut responder = NoiseSession::init_session(false, b"", receiver_static, None, psk); //let mut message_a_received: Vec = Vec::with_capacity(message_a_size); // you would have to make a sized buffer in implementation - receiver.recv_message(&mut message_a).unwrap(); // es derived internally + responder.recv_message(&mut message_a).unwrap(); // es derived internally println!("message_a bytes:{:?}", message_a); - let (_re, payload) = message_a.split_at_mut(DHLEN); + let (_initiator_e, payload) = message_a.split_at_mut(DHLEN); let (payload, _mac) = payload.split_at_mut(payload.len() - MAC_LENGTH); println!("message_a decrypted:{:?}", String::from_utf8(payload.to_vec())); // Ready Message B <- e, ee + // from the responder (payjoin receiver) + let payjoin_psbt = process_original_psbt(payload); + let mut in_out: Vec = vec![0; DHLEN]; + in_out.append(&mut payjoin_psbt.to_vec()); + let message_b_size = DHLEN + payjoin_psbt.len() + MAC_LENGTH; + in_out.resize(message_b_size, 0); + responder.send_message(&mut in_out).unwrap(); // e, ee + + // from the initiator (payjoin sender) + let mut message_b = in_out; + println!("message_b bytes:{:?}", message_b); + initiator.recv_message(&mut message_b).unwrap(); // ee derived internally + let (_responder_e, payload) = message_b.split_at_mut(DHLEN); + let (payload, _mac) = payload.split_at_mut(payload.len() - MAC_LENGTH); + println!("message_b decrypted:{:?}", String::from_utf8(payload.to_vec())); + } + + fn process_original_psbt(_original_psbt: &[u8]) -> &[u8] { + b"Payjoin PSBT" } } \ No newline at end of file From 7b95fa52d4be0807e26b84edcf07af4dc8618ab6 Mon Sep 17 00:00:00 2001 From: DanGould Date: Sat, 21 Jan 2023 15:39:31 -0500 Subject: [PATCH 17/24] Use NNpsk0, not NKpsk0 --- Cargo.lock | 16 ++++----- Cargo.toml | 2 +- {nkpsk0 => nnpsk0}/Cargo.toml | 2 +- {nkpsk0 => nnpsk0}/src/consts.rs | 0 {nkpsk0 => nnpsk0}/src/error.rs | 0 {nkpsk0 => nnpsk0}/src/lib.rs | 47 ++++++++++++++------------ {nkpsk0 => nnpsk0}/src/macros.rs | 0 {nkpsk0 => nnpsk0}/src/noisesession.rs | 11 ++---- {nkpsk0 => nnpsk0}/src/prims.rs | 0 {nkpsk0 => nnpsk0}/src/state.rs | 16 +++------ {nkpsk0 => nnpsk0}/src/types.rs | 16 ++++----- {nkpsk0 => nnpsk0}/src/utils.rs | 0 payjoin-client/Cargo.toml | 2 +- payjoin-client/src/main.rs | 12 ++----- 14 files changed, 55 insertions(+), 69 deletions(-) rename {nkpsk0 => nnpsk0}/Cargo.toml (88%) rename {nkpsk0 => nnpsk0}/src/consts.rs (100%) rename {nkpsk0 => nnpsk0}/src/error.rs (100%) rename {nkpsk0 => nnpsk0}/src/lib.rs (71%) rename {nkpsk0 => nnpsk0}/src/macros.rs (100%) rename {nkpsk0 => nnpsk0}/src/noisesession.rs (95%) rename {nkpsk0 => nnpsk0}/src/prims.rs (100%) rename {nkpsk0 => nnpsk0}/src/state.rs (94%) rename {nkpsk0 => nnpsk0}/src/types.rs (98%) rename {nkpsk0 => nnpsk0}/src/utils.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 15eff12ff..88a9a4f56 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -980,7 +980,13 @@ dependencies = [ ] [[package]] -name = "nkpsk0" +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "noiseexplorer_nnpsk0" version = "1.0.3" dependencies = [ "blake2-rfc", @@ -991,12 +997,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" - [[package]] name = "num_cpus" version = "1.15.0" @@ -1102,7 +1102,7 @@ dependencies = [ "base64", "bitcoincore-rpc 0.14.0", "clap", - "nkpsk0", + "noiseexplorer_nnpsk0", "payjoin", "reqwest", "tokio", diff --git a/Cargo.toml b/Cargo.toml index fb85cd16d..085099695 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,2 @@ [workspace] -members = ["payjoin", "payjoin-client", "payjoin-relay", "nkpsk0"] +members = ["payjoin", "payjoin-client", "payjoin-relay", "nnpsk0",] diff --git a/nkpsk0/Cargo.toml b/nnpsk0/Cargo.toml similarity index 88% rename from nkpsk0/Cargo.toml rename to nnpsk0/Cargo.toml index b473c7365..b107085fc 100644 --- a/nkpsk0/Cargo.toml +++ b/nnpsk0/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "nkpsk0" +name = "noiseexplorer_nnpsk0" version = "1.0.3" authors = ["Symbolic Software "] edition = "2018" diff --git a/nkpsk0/src/consts.rs b/nnpsk0/src/consts.rs similarity index 100% rename from nkpsk0/src/consts.rs rename to nnpsk0/src/consts.rs diff --git a/nkpsk0/src/error.rs b/nnpsk0/src/error.rs similarity index 100% rename from nkpsk0/src/error.rs rename to nnpsk0/src/error.rs diff --git a/nkpsk0/src/lib.rs b/nnpsk0/src/lib.rs similarity index 71% rename from nkpsk0/src/lib.rs rename to nnpsk0/src/lib.rs index 5f70e878c..e5580a72d 100644 --- a/nkpsk0/src/lib.rs +++ b/nnpsk0/src/lib.rs @@ -1,8 +1,6 @@ /* -NKpsk0: - <- s - ... - -> psk, e, es +NNpsk0: + -> psk, e <- e, ee -> <- @@ -24,40 +22,37 @@ pub mod error; pub mod noisesession; pub mod types; - #[cfg(test)] mod test { use crate::consts::{MAC_LENGTH, DHLEN}; use crate::noisesession::NoiseSession; - use crate::types::{Keypair, Psk, PublicKey}; + use crate::types::{Keypair, Psk}; #[test] - fn e2e_from_pj_sender() { - // <- s - let receiver_static = Keypair::default(); // s from responder aka payjoin receiver - let rs = receiver_static.get_public_key(); // rs from responder - + fn e2e_noise_from_pj_sender() { + let receiver_static = Keypair::new_empty(); // security does not depend on long-term static keys in NNpsk0. The interface still requires a Keypair, but it is not used. + // let receiver_static = Keypair::default(); // s from responder aka payjoin receiver // ... - let original_psbt = b"Original PSBT"; - println!("original_psbt: {:?}", String::from_utf8(original_psbt.to_vec())); + let psk = Psk::from_bytes(new_256bit_key()); // pre-shared from responder symmetric key. // todo can use 128 bit? // ... - - // Ready Message A -> psk, e, es - + + // Ready Message A -> psk, e + // from the initiator (payjoin sender) - let sender_static = Keypair::new_empty(); // the N in NK, should be nothing at all. - let psk = Psk::default(); // pre-shared (from WHO?) symmetric key. // TODO get from randomness, not default() emptyness - let mut initiator = NoiseSession::init_session(true, b"", sender_static, Some(rs), psk.clone()); + let original_psbt = b"Original PSBT"; + let sender_static = Keypair::new_empty(); // security does not depend on long-term static keys in NNpsk0. The interface still requires a Keypair, but it is not used. + let mut initiator = NoiseSession::init_session(true, b"", sender_static, psk.clone()); let mut in_out: Vec = vec![0; DHLEN]; in_out.append(&mut original_psbt.to_vec()); let message_a_size = DHLEN + original_psbt.len() + MAC_LENGTH; in_out.resize(message_a_size, 0); - initiator.send_message(&mut in_out).unwrap(); // psk, e, es + initiator.send_message(&mut in_out).unwrap(); // psk, e let mut message_a = in_out; // from the responder (payjoin receiver) - let mut responder = NoiseSession::init_session(false, b"", receiver_static, None, psk); + + let mut responder = NoiseSession::init_session(false, b"", receiver_static, psk); //let mut message_a_received: Vec = Vec::with_capacity(message_a_size); // you would have to make a sized buffer in implementation responder.recv_message(&mut message_a).unwrap(); // es derived internally println!("message_a bytes:{:?}", message_a); @@ -86,4 +81,14 @@ mod test { fn process_original_psbt(_original_psbt: &[u8]) -> &[u8] { b"Payjoin PSBT" } + + fn new_256bit_key() -> [u8; 32] { + use rand::RngCore; + use rand::thread_rng; + + let mut rng = thread_rng(); + let mut key = [0u8; 32]; + rng.fill_bytes(&mut key); + key + } } \ No newline at end of file diff --git a/nkpsk0/src/macros.rs b/nnpsk0/src/macros.rs similarity index 100% rename from nkpsk0/src/macros.rs rename to nnpsk0/src/macros.rs diff --git a/nkpsk0/src/noisesession.rs b/nnpsk0/src/noisesession.rs similarity index 95% rename from nkpsk0/src/noisesession.rs rename to nnpsk0/src/noisesession.rs index a386e844a..951f388c8 100644 --- a/nkpsk0/src/noisesession.rs +++ b/nnpsk0/src/noisesession.rs @@ -103,12 +103,12 @@ impl NoiseSession { /// - `initiator`: `bool` variable. To be set as `true` when initiating a handshake with a remote party, or `false` otherwise. /// - `prologue`: `Message` object. Could optionally contain the name of the protocol to be used. /// - `s`: `Keypair` object. Contains local party's static keypair. - /// - `rs`: `Option`. Contains the remote party's static public key. Tip: use `Some(rs_value)` in case a remote static key exists and `None` otherwise. + /// - `psk`: `Psk` object. Contains the pre-shared key. - pub fn init_session(initiator: bool, prologue: &[u8], s: Keypair, rs: Option, psk: Psk) -> NoiseSession { + pub fn init_session(initiator: bool, prologue: &[u8], s: Keypair, psk: Psk) -> NoiseSession { if initiator { NoiseSession{ - hs: HandshakeState::initialize_initiator(prologue, s, rs.unwrap_or(PublicKey::empty()), psk), + hs: HandshakeState::initialize_initiator(prologue, s, psk), mc: 0, i: initiator, cs1: CipherState::new(), @@ -151,13 +151,10 @@ impl NoiseSession { self.cs1 = temp.1; self.cs2 = temp.2; self.hs.clear(); - } else if self.i { self.cs1.write_message_regular(in_out)?; - } else { self.cs2.write_message_regular(in_out)?; - } self.mc += 1; Ok(()) @@ -186,10 +183,8 @@ impl NoiseSession { self.cs1 = temp.1; self.cs2 = temp.2; self.hs.clear(); - } else if self.i { self.cs2.read_message_regular(in_out)?; - } else { self.cs1.read_message_regular(in_out)?; } diff --git a/nkpsk0/src/prims.rs b/nnpsk0/src/prims.rs similarity index 100% rename from nkpsk0/src/prims.rs rename to nnpsk0/src/prims.rs diff --git a/nkpsk0/src/state.rs b/nnpsk0/src/state.rs similarity index 94% rename from nkpsk0/src/state.rs rename to nnpsk0/src/state.rs index d667a56bf..8cd2b2229 100644 --- a/nkpsk0/src/state.rs +++ b/nnpsk0/src/state.rs @@ -198,7 +198,6 @@ impl SymmetricState { pub(crate) fn encrypt_and_hash(&mut self, in_out: &mut [u8]) -> Result<(), NoiseError> { let mut temp_mac: [u8; MAC_LENGTH] = [0u8;MAC_LENGTH]; let (plaintext, mac) = in_out.split_at_mut(in_out.len()-MAC_LENGTH); - println!("encrypt_and_hash: plaintext: {:?}", plaintext); self.cs.encrypt_with_ad(&self.h.as_bytes()[..], plaintext, &mut temp_mac)?; mac.copy_from_slice(&temp_mac[..]); self.mix_hash(in_out); @@ -264,20 +263,19 @@ impl HandshakeState { self.e = e; } - pub(crate) fn initialize_initiator(prologue: &[u8], s: Keypair, rs: PublicKey, psk: Psk) -> HandshakeState { - let protocol_name = b"Noise_NKpsk0_25519_ChaChaPoly_BLAKE2s"; + pub(crate) fn initialize_initiator(prologue: &[u8], s: Keypair, psk: Psk) -> HandshakeState { + let protocol_name = b"Noise_NNpsk0_25519_ChaChaPoly_BLAKE2s"; let mut ss: SymmetricState = SymmetricState::initialize_symmetric(&protocol_name[..]); ss.mix_hash(prologue); - ss.mix_hash(&rs.as_bytes()[..]); + let rs = PublicKey::empty(); HandshakeState{ss, s, e: Keypair::new_empty(), rs, re: PublicKey::empty(), psk} } pub(crate) fn initialize_responder(prologue: &[u8], s: Keypair, psk: Psk) -> HandshakeState { - let protocol_name = b"Noise_NKpsk0_25519_ChaChaPoly_BLAKE2s"; + let protocol_name = b"Noise_NNpsk0_25519_ChaChaPoly_BLAKE2s"; let mut ss: SymmetricState = SymmetricState::initialize_symmetric(&protocol_name[..]); ss.mix_hash(prologue); let rs = PublicKey::empty(); - ss.mix_hash(&s.get_public_key().as_bytes()[..]); HandshakeState{ss, s, e: Keypair::new_empty(), rs, re: PublicKey::empty(), psk} } pub(crate) fn write_message_a(&mut self, in_out: &mut [u8]) -> Result<(), NoiseError> { @@ -287,14 +285,11 @@ impl HandshakeState { } if self.e.is_empty() { self.e = Keypair::default(); - println!("e is empty, generating new keypair"); } let (ne, in_out) = in_out.split_at_mut(DHLEN); - println!("in_out: {:?} this should be the payload + MAC", in_out); ne.copy_from_slice(&self.e.get_public_key().as_bytes()[..]); self.ss.mix_hash(ne); self.ss.mix_key(&self.e.get_public_key().as_bytes()); - self.ss.mix_key(&self.e.dh(&self.rs.as_bytes())); self.ss.encrypt_and_hash(in_out)?; Ok(()) } @@ -325,13 +320,10 @@ impl HandshakeState { return Err(NoiseError::MissingreError); } let (re, in_out) = in_out.split_at_mut(DHLEN); - println!("re: {:?}", re); // DH self.re = PublicKey::from_bytes(from_slice_hashlen(re))?; self.ss.mix_hash(&self.re.as_bytes()[..DHLEN]); self.ss.mix_key(&self.re.as_bytes()); - self.ss.mix_key(&self.s.dh(&self.re.as_bytes())); self.ss.decrypt_and_hash(in_out)?; - Ok(()) } diff --git a/nkpsk0/src/types.rs b/nnpsk0/src/types.rs similarity index 98% rename from nkpsk0/src/types.rs rename to nnpsk0/src/types.rs index f0977b32e..17aa0ba84 100644 --- a/nkpsk0/src/types.rs +++ b/nnpsk0/src/types.rs @@ -72,7 +72,7 @@ impl Key { /// # Example /// /// ``` - /// # use noiseexplorer_nkpsk0::{ + /// # use noiseexplorer_nnpsk0::{ /// # error::NoiseError, /// # types::Key, /// # }; @@ -109,7 +109,7 @@ impl std::str::FromStr for Key { /// # Example /// /// ``` - /// # use noiseexplorer_nkpsk0::{ + /// # use noiseexplorer_nnpsk0::{ /// # error::NoiseError, /// # types::Key, /// # }; @@ -154,7 +154,7 @@ impl Psk { /// # Example /// /// ``` - /// # use noiseexplorer_nkpsk0::{ + /// # use noiseexplorer_nnpsk0::{ /// # error::NoiseError, /// # types::Psk, /// # }; @@ -190,7 +190,7 @@ impl std::str::FromStr for Psk { /// # Example /// /// ``` - /// # use noiseexplorer_nkpsk0::{ + /// # use noiseexplorer_nnpsk0::{ /// # error::NoiseError, /// # types::Psk, /// # }; @@ -241,7 +241,7 @@ impl PrivateKey { /// # Example /// /// ``` - /// # use noiseexplorer_nkpsk0::{ + /// # use noiseexplorer_nnpsk0::{ /// # error::NoiseError, /// # types::PrivateKey, /// # }; @@ -279,7 +279,7 @@ impl std::str::FromStr for PrivateKey { /// # Example /// /// ``` - /// # use noiseexplorer_nkpsk0::{ + /// # use noiseexplorer_nnpsk0::{ /// # error::NoiseError, /// # types::PrivateKey, /// # }; @@ -336,7 +336,7 @@ impl PublicKey { /// # Example /// /// ``` - /// # use noiseexplorer_nkpsk0::{ + /// # use noiseexplorer_nnpsk0::{ /// # error::NoiseError, /// # types::PublicKey, /// # }; @@ -363,7 +363,7 @@ impl PublicKey { /// # Example /// /// ``` - /// # use noiseexplorer_nkpsk0::{ + /// # use noiseexplorer_nnpsk0::{ /// # error::NoiseError, /// # types::PublicKey, /// # }; diff --git a/nkpsk0/src/utils.rs b/nnpsk0/src/utils.rs similarity index 100% rename from nkpsk0/src/utils.rs rename to nnpsk0/src/utils.rs diff --git a/payjoin-client/Cargo.toml b/payjoin-client/Cargo.toml index ebf608974..7dce99d91 100644 --- a/payjoin-client/Cargo.toml +++ b/payjoin-client/Cargo.toml @@ -15,4 +15,4 @@ clap = "2.33.3" turn = "0.6.1" tokio = { version = "1.6.1", features = ["full"] } webrtc-util = { version = "0.7.0", default-features = false, features = ["conn", "vnet"] } -nkpsk0 = { path = "../nkpsk0" } \ No newline at end of file +noiseexplorer_nnpsk0 = { path = "../nnpsk0" } \ No newline at end of file diff --git a/payjoin-client/src/main.rs b/payjoin-client/src/main.rs index 9657053a6..244a02202 100644 --- a/payjoin-client/src/main.rs +++ b/payjoin-client/src/main.rs @@ -6,11 +6,6 @@ use std::str::FromStr; use std::sync::Arc; use bitcoincore_rpc::bitcoin::util::psbt::PartiallySignedTransaction; -use nkpsk0::consts::DHLEN; -use nkpsk0::consts::MAC_LENGTH; -use nkpsk0::types::Keypair; -use nkpsk0::types::Psk; -use nkpsk0::types::PublicKey; use payjoin::bitcoin::hashes::hex::ToHex; use turn::client::*; use turn::Error; @@ -24,7 +19,6 @@ use tokio::net::UdpSocket; use tokio::time::Duration; use webrtc_util::Conn; -use nkpsk0::noisesession::NoiseSession; #[tokio::main] async fn main() -> Result<(), Error> { @@ -98,6 +92,7 @@ async fn main() -> Result<(), Error> { } else { let bip21 = matches.value_of("bip21").unwrap().as_ref(); let endpoint = matches.value_of("endpoint").unwrap(); + let rs = matches.value_of("rs").unwrap(); let (req, ctx) = create_pj_request(bip21, &bitcoind); //base64 request let payjoin_psbt = do_send(req, ctx, endpoint).await?; let psbt = bitcoind.wallet_process_psbt(&serialize_psbt(&payjoin_psbt), None, None, None).unwrap().psbt; @@ -223,9 +218,7 @@ async fn process_original_psbt( Psbt::from_unsigned_tx(payjoin_proposal_psbt.unsigned_tx.clone()) .expect("resetting tx failed"); let payjoin_proposal_psbt = base64::encode(consensus::serialize(&payjoin_proposal_psbt)); - //payjoin_proposal_psbt.resize(payjoin_proposal_psbt.len() + DHLEN + MAC_LENGTH, 0u8); relay_conn.send_to(payjoin_proposal_psbt.as_bytes(), from).await?; - println!("sent PayJoin Proposal"); Ok(()) } @@ -236,6 +229,7 @@ async fn do_send(req: payjoin::sender::Request, ctx: payjoin::sender::Context, r // Set up pinger socket (pingerConn) let pinger_conn_tx = Arc::new(UdpSocket::bind("0.0.0.0:0").await?); let pinger_conn_rx = Arc::clone(&pinger_conn_tx); + pinger_conn_tx.send_to(msg.as_slice(), relay_addr).await?; println!("len: {} < 1024?", msg.len()); @@ -342,4 +336,4 @@ impl payjoin::receiver::Headers for MockHeaders { _ => None, } } -} \ No newline at end of file +} From 4b5d30e6aa6b8d77102fa5da00786801024aef6d Mon Sep 17 00:00:00 2001 From: DanGould Date: Sat, 21 Jan 2023 17:41:58 -0500 Subject: [PATCH 18/24] PayJoin e2e over relay with noise --- Cargo.lock | 1 + payjoin-client/Cargo.toml | 1 + payjoin-client/src/main.rs | 87 +++++++++++++++++++++++++++++--------- 3 files changed, 70 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 88a9a4f56..0a92cf15c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1104,6 +1104,7 @@ dependencies = [ "clap", "noiseexplorer_nnpsk0", "payjoin", + "rand 0.6.5", "reqwest", "tokio", "turn", diff --git a/payjoin-client/Cargo.toml b/payjoin-client/Cargo.toml index 7dce99d91..2cb1c95fa 100644 --- a/payjoin-client/Cargo.toml +++ b/payjoin-client/Cargo.toml @@ -9,6 +9,7 @@ edition = "2018" [dependencies] payjoin = { path = "../payjoin", features = ["sender", "receiver"] } bitcoincore-rpc = "0.14.0" +rand = "0.6.5" reqwest = { version = "0.11.4", features = ["blocking"] } base64 = "0.13.0" clap = "2.33.3" diff --git a/payjoin-client/src/main.rs b/payjoin-client/src/main.rs index 244a02202..263e8aa80 100644 --- a/payjoin-client/src/main.rs +++ b/payjoin-client/src/main.rs @@ -1,11 +1,9 @@ use std::collections::HashMap; use std::convert::TryFrom; use std::convert::TryInto; -use std::net::SocketAddr; use std::str::FromStr; use std::sync::Arc; -use bitcoincore_rpc::bitcoin::util::psbt::PartiallySignedTransaction; use payjoin::bitcoin::hashes::hex::ToHex; use turn::client::*; use turn::Error; @@ -16,8 +14,10 @@ use clap::{App, AppSettings, Arg}; use payjoin::bitcoin::util::psbt::PartiallySignedTransaction as Psbt; use payjoin::{PjUriExt, UriExt}; use tokio::net::UdpSocket; -use tokio::time::Duration; use webrtc_util::Conn; +use noiseexplorer_nnpsk0::noisesession::NoiseSession; +use noiseexplorer_nnpsk0::consts::{MAC_LENGTH, DHLEN}; +use noiseexplorer_nnpsk0::types::Keypair; #[tokio::main] @@ -51,10 +51,10 @@ async fn main() -> Result<(), Error> { .long("endpoint") .help("The endpoint to send the payjoin to") .takes_value(true)) - .arg(Arg::with_name("rs") + .arg(Arg::with_name("psk") .short("s") - .long("rs") - .help("The receiver's static public key") + .long("psk") + .help("The pre-shared symmetric key") .takes_value(true)) .arg(Arg::with_name("relay") @@ -92,9 +92,9 @@ async fn main() -> Result<(), Error> { } else { let bip21 = matches.value_of("bip21").unwrap().as_ref(); let endpoint = matches.value_of("endpoint").unwrap(); - let rs = matches.value_of("rs").unwrap(); + let psk = matches.value_of("psk").unwrap(); let (req, ctx) = create_pj_request(bip21, &bitcoind); //base64 request - let payjoin_psbt = do_send(req, ctx, endpoint).await?; + let payjoin_psbt = do_send(req, ctx, endpoint, psk).await?; let psbt = bitcoind.wallet_process_psbt(&serialize_psbt(&payjoin_psbt), None, None, None).unwrap().psbt; let tx = bitcoind.finalize_psbt(&psbt, Some(true)).unwrap().hex.expect("incomplete psbt"); let txid = bitcoind.send_raw_transaction(&tx).unwrap(); @@ -137,7 +137,8 @@ async fn listen_receiver(relay: &str, amount: Amount, bitcoind: bitcoincore_rpc: // The relayConn's local address is actually the transport // address assigned on the TURN server. let relay_conn_endpoint = relay_conn.local_addr()?; - print!("--endpoint={} ", relay_conn_endpoint); + let psk = gen_psk(); + print!("--endpoint={} --psk=\"{}\" ", relay_conn_endpoint, psk); let mapped_addr = client.send_binding_request().await?; // punch UDP hole. after this packets from the IP address will be accepted by the turn server @@ -146,7 +147,7 @@ async fn listen_receiver(relay: &str, amount: Amount, bitcoind: bitcoincore_rpc: // 2. Recv let pj_receiver_address = bitcoind.get_new_address(None, Some(bitcoincore_rpc::json::AddressType::Bech32)).unwrap(); print_payjoin_uri(pj_receiver_address, amount); - process_original_psbt(relay_conn, bitcoind).await?; + process_original_psbt(relay_conn, bitcoind, psk).await?; client.close().await?; Ok(()) @@ -155,6 +156,7 @@ async fn listen_receiver(relay: &str, amount: Amount, bitcoind: bitcoincore_rpc: async fn process_original_psbt( relay_conn: impl Conn + std::marker::Send + std::marker::Sync + 'static, receiver: bitcoincore_rpc::Client, + psk: String, ) -> Result<(), Error> { use payjoin::bitcoin; use payjoin::receiver::UncheckedProposal; @@ -162,16 +164,24 @@ async fn process_original_psbt( use payjoin::bitcoin::blockdata::script::Script; use payjoin::bitcoin::blockdata::transaction::TxOut; + let psk: [u8; 32] = base64::decode(psk).unwrap().try_into().unwrap(); + let psk = noiseexplorer_nnpsk0::types::Psk::from_bytes(psk); + let mut buf = [0u8; 1024]; let (n, from) = relay_conn.recv_from(&mut buf).await?; println!("received {} bytes of supposed Original PSBT from {}", n, from); - - + println!("buf: {:?}", &buf[..n]); + // security does not depend on long-term static keys in NNpsk0. The interface still requires a Keypair, but it is not used. + let mut responder = NoiseSession::init_session(false, b"", Keypair::new_empty(), psk); + responder.recv_message(&mut buf[..n]).unwrap(); // es derived internally + let (_initiator_e, payload) = buf.split_at_mut(DHLEN); + let (payload, _mac) = payload.split_at_mut(payload.len() - MAC_LENGTH); + let n = payload.len(); // hopefully from_request can trim the padding, else we're gonna need to send the length on the wire as headers does // query, headers not passed by default udp // We'll need to figure out how to pass query info at least. let query = ""; let headers = MockHeaders::new(n); - let proposal = UncheckedProposal::from_request(&buf[..n], query, headers).unwrap(); + let proposal = UncheckedProposal::from_request(&payload[..n], query, headers).unwrap(); // Receive Check 1: Is Broadcastable let original_tx = proposal.get_transaction_to_check_broadcast(); @@ -218,25 +228,54 @@ async fn process_original_psbt( Psbt::from_unsigned_tx(payjoin_proposal_psbt.unsigned_tx.clone()) .expect("resetting tx failed"); let payjoin_proposal_psbt = base64::encode(consensus::serialize(&payjoin_proposal_psbt)); - relay_conn.send_to(payjoin_proposal_psbt.as_bytes(), from).await?; + + // noise handshake response + let mut in_out: Vec = vec![0; DHLEN]; + let message_b_size = DHLEN + payjoin_proposal_psbt.len() + MAC_LENGTH; + in_out.append(&mut payjoin_proposal_psbt.as_bytes().to_vec()); + in_out.resize(message_b_size, 0); + responder.send_message(&mut in_out).unwrap(); // e, ee + + relay_conn.send_to(in_out.as_slice(), from).await?; Ok(()) } -async fn do_send(req: payjoin::sender::Request, ctx: payjoin::sender::Context, relay_addr: &str) -> Result { - let msg = req.body; //format!("{:?}", tokio::time::Instant::now()); +async fn do_send(req: payjoin::sender::Request, ctx: payjoin::sender::Context, relay_addr: &str, psk: &str) -> Result { + let mut original_psbt = req.body.clone(); //format!("{:?}", tokio::time::Instant::now()); // Set up pinger socket (pingerConn) let pinger_conn_tx = Arc::new(UdpSocket::bind("0.0.0.0:0").await?); let pinger_conn_rx = Arc::clone(&pinger_conn_tx); - pinger_conn_tx.send_to(msg.as_slice(), relay_addr).await?; + // do noise secure handshake + let psk: [u8; 32] = base64::decode(psk).unwrap().try_into().unwrap(); + println!("psk: {:?}", base64::encode(psk)); + let psk = noiseexplorer_nnpsk0::types::Psk::from_bytes(psk); + + // security does not depend on long-term static keys in NNpsk0. The interface still requires a Keypair, but it is not used. + let mut initiator = NoiseSession::init_session(true, b"", Keypair::new_empty(), psk.clone()); + let mut in_out: Vec = vec![0; DHLEN]; + let message_a_size = DHLEN + original_psbt.len() + MAC_LENGTH; + in_out.append(&mut original_psbt); + in_out.resize(message_a_size, 0); + println!("sending in_out: {:?}", in_out); + initiator.send_message(&mut in_out).unwrap(); // psk, e + println!("sending message_a: {:?}", in_out); + pinger_conn_tx.send_to(in_out.as_slice(), relay_addr).await?; + - println!("len: {} < 1024?", msg.len()); + println!("len: {} < 1024?", req.body.len()); let mut buf = [0u8; 1024]; let (n, from) = pinger_conn_rx.recv_from(&mut buf).await?; - let proposal = ctx.process_response(&buf[..n]).unwrap(); + + // finish noise handshake + initiator.recv_message(&mut buf[..n]).unwrap(); + let (_responder_e, payload) = buf.split_at_mut(DHLEN); + let (payload, _mac) = payload.split_at_mut(payload.len() - MAC_LENGTH); + let n = payload.len(); + let proposal = ctx.process_response(&payload[..n]).unwrap(); println!("proposal: {:#?} from {}", proposal, from); Ok(proposal) } @@ -337,3 +376,13 @@ impl payjoin::receiver::Headers for MockHeaders { } } } + +fn gen_psk() -> String { + use rand::RngCore; + use rand::thread_rng; + + let mut rng = thread_rng(); + let mut key = [0u8; 32]; + rng.fill_bytes(&mut key); + base64::encode(&key) +} \ No newline at end of file From 9cafb581f97d814496a369a8f242aa9741599359 Mon Sep 17 00:00:00 2001 From: DanGould Date: Thu, 2 Feb 2023 09:41:19 -0500 Subject: [PATCH 19/24] Move cookie_file cookie-file arg --- payjoin-client/src/main.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/payjoin-client/src/main.rs b/payjoin-client/src/main.rs index 263e8aa80..8a2682d61 100644 --- a/payjoin-client/src/main.rs +++ b/payjoin-client/src/main.rs @@ -34,9 +34,9 @@ async fn main() -> Result<(), Error> { .help("The bitcoind rpc port to connect to") .takes_value(true) .required(true)) - .arg(Arg::with_name("cookie_file") + .arg(Arg::with_name("cookie-file") .short("c") - .long("cookie_file") + .long("cookie-file") .help("The bitcoind rpc cookie file to use for authentication") .takes_value(true) .required(true)) @@ -76,7 +76,7 @@ async fn main() -> Result<(), Error> { } let port = matches.value_of("port").unwrap(); - let cookie_file = matches.value_of("cookie_file").unwrap(); + let cookie_file = matches.value_of("cookie-file").unwrap(); let bitcoind = bitcoincore_rpc::Client::new( &format!("http://127.0.0.1:{}", port), From 8ab47f72add101ebe07debbd64b0ce5393dd3441 Mon Sep 17 00:00:00 2001 From: DanGould Date: Thu, 2 Feb 2023 09:41:37 -0500 Subject: [PATCH 20/24] Write integration instructions --- payjoin-relay/README.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 payjoin-relay/README.md diff --git a/payjoin-relay/README.md b/payjoin-relay/README.md new file mode 100644 index 000000000..d756f7d55 --- /dev/null +++ b/payjoin-relay/README.md @@ -0,0 +1,41 @@ +# Payjoin Relay + +This is a simple TURN relay for payjoin clients to connect peer-to-peer. + +The payjoin client sends and receives payjoins for a configured bitcoin rpc client wallet. In its current form it demonstrates Serverless Payjoin capabilities by communicating over p2p UDP extablished using TURN. + +## Demo + +Follow along to see how it works + +### Start a relay + +This relay has permission for a "receiver" user to request an allocation with password "test" + +```console +RUST_LOG=trace cargo run --bin payjoin-relay -- --public-ip 0.0.0.0 --users receiver=test +``` + +### Start the recipient + +Connect the server to a bitcoind rpc host and specify the amount you would like to request, in sats + +```console +RUST_LOG=trace cargo run --bin payjoin-client -- --port=18444 --cookie-file="/Users/dan/.polar/networks/1/volumes/bitcoind/backend2/regtest/.cookie" --amount=200000 --relay=0.0.0.0:3478 +``` + +The server should output arguments for the client + +```console +--endpoint=0.0.0.0:61629 --psk="EFA92zD3PjUlUg5uX/rsQ/+SFkJi3V3HGKi3H6WWw8I=" --bip21="BITCOIN:BCRT1QG8ZWJWLT640CJ076VLTDLKS2GJCF596XLYER95?amount=0.002&pj=https://example.com" +``` + +### Send the payjoin + +With the sender arguments in hand, we can send from a second funded bitcoind rpc host + +```console +RUST_LOG=trace cargo run --bin payjoin-client -- --port=18443 --cookie-file="/Users/dan/.polar/networks/1/volumes/bitcoind/backend1/regtest/.cookie" --endpoint=0.0.0.0:61629 --psk="EFA92zD3PjUlUg5uX/rsQ/+SFkJi3V3HGKi3H6WWw8I=" --bip21="BITCOIN:BCRT1QG8ZWJWLT640CJ076VLTDLKS2GJCF596XLYER95?amount=0.002&pj=https://example.com" +``` + +When the chain advances, the new payjoin transaction should confirm. From d21dfe173910055a29ea6bab3ea811c560fd7cae Mon Sep 17 00:00:00 2001 From: DanGould Date: Wed, 8 Feb 2023 12:34:19 -0500 Subject: [PATCH 21/24] Default to pj.chaincase.app realm --- payjoin-client/src/main.rs | 2 +- payjoin-relay/src/main.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/payjoin-client/src/main.rs b/payjoin-client/src/main.rs index 8a2682d61..cd354fc53 100644 --- a/payjoin-client/src/main.rs +++ b/payjoin-client/src/main.rs @@ -117,7 +117,7 @@ async fn listen_receiver(relay: &str, amount: Amount, bitcoind: bitcoincore_rpc: turn_serv_addr: turn_server_addr, username: "receiver".to_string(), password: "test".to_string(), - realm: "test".to_string(), + realm: "pj.chaincase.app".to_string(), software: String::new(), rto_in_ms: 0, conn: Arc::new(conn), diff --git a/payjoin-relay/src/main.rs b/payjoin-relay/src/main.rs index 150bfa40d..9bac33961 100644 --- a/payjoin-relay/src/main.rs +++ b/payjoin-relay/src/main.rs @@ -72,10 +72,10 @@ async fn main() -> Result<(), Error> { ) .arg( Arg::with_name("realm") - .default_value("webrtc.rs") + .default_value("pj.chaincase.app") .takes_value(true) .long("realm") - .help("Realm (defaults to \"webrtc.rs\")"), + .help("Realm (defaults to \"pj.chaincase.app\")"), ) .arg( Arg::with_name("port") From 16ca3da42d4d4daee4705171368720348f96bb0d Mon Sep 17 00:00:00 2001 From: DanGould Date: Wed, 8 Feb 2023 12:35:57 -0500 Subject: [PATCH 22/24] Generate relay ports from default range --- payjoin-relay/src/main.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/payjoin-relay/src/main.rs b/payjoin-relay/src/main.rs index 9bac33961..76557c297 100644 --- a/payjoin-relay/src/main.rs +++ b/payjoin-relay/src/main.rs @@ -1,5 +1,5 @@ use turn::auth::*; -use turn::relay::relay_static::*; +use turn::relay::relay_range::RelayAddressGeneratorRanges; use turn::server::{config::*, *}; use turn::Error; @@ -116,8 +116,11 @@ async fn main() -> Result<(), Error> { let server = Server::new(ServerConfig { conn_configs: vec![ConnConfig { conn, - relay_addr_generator: Box::new(RelayAddressGeneratorStatic { + relay_addr_generator: Box::new(RelayAddressGeneratorRanges { relay_address: IpAddr::from_str(public_ip)?, + min_port: 49152, + max_port: 65535, + max_retries: 10, address: "0.0.0.0".to_owned(), net: Arc::new(Net::new(None)), }), From 66a86e9172622715426f4f78960463ab93836123 Mon Sep 17 00:00:00 2001 From: DanGould Date: Thu, 16 Feb 2023 14:36:42 -0500 Subject: [PATCH 23/24] Replace TURN with Wormhole --- Cargo.lock | 1090 +++++++++++++++++++++++++++++++++++- payjoin-client.Dockerfile | 28 + payjoin-client/Cargo.toml | 5 +- payjoin-client/README.md | 37 ++ payjoin-client/src/main.rs | 107 +--- payjoin-relay.Dockerfile | 28 + payjoin-relay/Cargo.toml | 2 +- payjoin-relay/README.md | 41 -- 8 files changed, 1197 insertions(+), 141 deletions(-) create mode 100644 payjoin-client.Dockerfile create mode 100644 payjoin-client/README.md create mode 100644 payjoin-relay.Dockerfile delete mode 100644 payjoin-relay/README.md diff --git a/Cargo.lock b/Cargo.lock index 0a92cf15c..e776b9b02 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,42 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aead" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" +dependencies = [ + "generic-array", + "rand_core 0.6.4", +] + +[[package]] +name = "aes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", + "opaque-debug", +] + +[[package]] +name = "aes-gcm" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "aho-corasick" version = "0.7.20" @@ -35,6 +71,171 @@ dependencies = [ "nodrop", ] +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "async-attributes" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "async-channel" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-executor" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17adb73da160dfb475c183343c8cccd80721ea5a605d3eb57125f0a7b7a92d0b" +dependencies = [ + "async-lock", + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-global-executor" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" +dependencies = [ + "async-channel", + "async-executor", + "async-io", + "async-lock", + "blocking", + "futures-lite", + "once_cell", +] + +[[package]] +name = "async-io" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c374dda1ed3e7d8f0d9ba58715f924862c63eae6849c92d3a18e7fbde9e2794" +dependencies = [ + "async-lock", + "autocfg 1.1.0", + "concurrent-queue", + "futures-lite", + "libc", + "log", + "parking", + "polling", + "slab", + "socket2", + "waker-fn", + "windows-sys 0.42.0", +] + +[[package]] +name = "async-lock" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8101efe8695a6c17e02911402145357e718ac92d3ff88ae8419e84b1707b685" +dependencies = [ + "event-listener", + "futures-lite", +] + +[[package]] +name = "async-process" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6381ead98388605d0d9ff86371043b5aa922a3905824244de40dc263a14fcba4" +dependencies = [ + "async-io", + "async-lock", + "autocfg 1.1.0", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "libc", + "signal-hook", + "windows-sys 0.42.0", +] + +[[package]] +name = "async-std" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" +dependencies = [ + "async-attributes", + "async-channel", + "async-global-executor", + "async-io", + "async-lock", + "async-process", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-tar" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c49359998a76e32ef6e870dbc079ebad8f1e53e8441c5dd39d27b44493fe331" +dependencies = [ + "async-std", + "filetime", + "libc", + "pin-project", + "redox_syscall", + "xattr", +] + +[[package]] +name = "async-task" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" + +[[package]] +name = "async-tls" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f23d769dbf1838d5df5156e7b1ad404f4c463d1ac2c6aeb6cd943630f8a8400" +dependencies = [ + "futures-core", + "futures-io", + "rustls", + "webpki", + "webpki-roots", +] + [[package]] name = "async-trait" version = "0.1.61" @@ -46,6 +247,27 @@ dependencies = [ "syn", ] +[[package]] +name = "async-tungstenite" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1b71b31561643aa8e7df3effe284fa83ab1a840e52294c5f4bd7bfd8b2becbb" +dependencies = [ + "async-std", + "async-tls", + "futures-io", + "futures-util", + "log", + "pin-project-lite", + "tungstenite", +] + +[[package]] +name = "atomic-waker" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "debc29dde2e69f9e47506b525f639ed42300fc014a3e007832592448fa8e4599" + [[package]] name = "atty" version = "0.2.14" @@ -84,6 +306,12 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5" + [[package]] name = "base64-compat" version = "1.0.0" @@ -229,13 +457,22 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.6", +] + [[package]] name = "blake2-rfc" version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" dependencies = [ - "arrayvec", + "arrayvec 0.4.12", "constant_time_eq", ] @@ -248,12 +485,36 @@ dependencies = [ "generic-array", ] +[[package]] +name = "blocking" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c67b173a56acffd6d2326fb7ab938ba0b00a71480e14902b2591c87bc5741e8" +dependencies = [ + "async-channel", + "async-lock", + "async-task", + "atomic-waker", + "fastrand", + "futures-lite", +] + [[package]] name = "bumpalo" version = "3.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +[[package]] +name = "bytecodec" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adf4c9d0bbf32eea58d7c0f812058138ee8edaf0f2802b6d03561b504729a325" +dependencies = [ + "byteorder", + "trackable 0.2.24", +] + [[package]] name = "byteorder" version = "1.4.3" @@ -287,6 +548,12 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "cache-padded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" + [[package]] name = "cc" version = "1.0.78" @@ -299,12 +566,46 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chacha20" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c80e5460aa66fe3b91d40bcbdab953a597b60053e34d684ac6903f863b680a6" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", + "zeroize", +] + +[[package]] +name = "chacha20poly1305" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18446b09be63d457bbec447509e85f662f32952b035ce892290396bc0b0cff5" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + [[package]] name = "chunked_transfer" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cca491388666e04d7248af3f60f0c40cfb0991c72205595d7c396e3510207d1a" +[[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array", +] + [[package]] name = "clap" version = "2.34.0" @@ -314,7 +615,7 @@ dependencies = [ "ansi_term", "atty", "bitflags", - "strsim", + "strsim 0.8.0", "textwrap", "unicode-width", "vec_map", @@ -329,6 +630,15 @@ dependencies = [ "bitflags", ] +[[package]] +name = "concurrent-queue" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c278839b831783b70278b14df4d45e1beb1aad306c07bb796637de9a0e323e8e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "const_fn" version = "0.4.9" @@ -384,15 +694,39 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49fc9a695bca7f35f5f4c15cddc84415f66a74ea78eef08e90c5024f2b540e23" +dependencies = [ + "crc-catalog 1.1.1", +] + [[package]] name = "crc" version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53757d12b596c16c78b83458d732a5d1a17ab3f53f2f7412f6fb57cc8a140ab3" dependencies = [ - "crc-catalog", + "crc-catalog 2.2.0", ] +[[package]] +name = "crc-catalog" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccaeedb56da03b09f598226e25e80088cb4cd25f316e6e4df7d695f0feeb1403" + [[package]] name = "crc-catalog" version = "2.2.0" @@ -408,6 +742,15 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-utils" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" +dependencies = [ + "cfg-if", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -418,6 +761,93 @@ dependencies = [ "typenum", ] +[[package]] +name = "ctor" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "ctr" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" +dependencies = [ + "cipher", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f9d052967f590a76e62eb387bd0bbb1b000182c3cefe5364db6b7211651bc0" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "darling" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.9.3", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + [[package]] name = "digest" version = "0.10.6" @@ -426,6 +856,7 @@ checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ "block-buffer", "crypto-common", + "subtle", ] [[package]] @@ -462,6 +893,12 @@ dependencies = [ "termcolor", ] +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + [[package]] name = "fastrand" version = "1.8.0" @@ -579,6 +1016,21 @@ version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" +[[package]] +name = "futures-lite" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + [[package]] name = "futures-macro" version = "0.3.25" @@ -620,6 +1072,19 @@ dependencies = [ "slab", ] +[[package]] +name = "futures_ringbuf" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b905098b5519bd63b2a1f9f4615198b0e38a473ce201ffdbd4dea6eb63087ddc" +dependencies = [ + "futures", + "log", + "log-derive", + "ringbuf", + "rustc_version 0.4.0", +] + [[package]] name = "generic-array" version = "0.14.6" @@ -630,6 +1095,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + [[package]] name = "getrandom" version = "0.2.8" @@ -641,6 +1117,28 @@ dependencies = [ "wasi 0.11.0+wasi-snapshot-preview1", ] +[[package]] +name = "ghash" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + [[package]] name = "h2" version = "0.3.15" @@ -709,6 +1207,36 @@ name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] + +[[package]] +name = "hkdf" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "hmac-sha1" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1333fad8d94b82cab989da428b0b36a3435db3870d85e971a1d6dc0a8576722" +dependencies = [ + "sha1 0.2.0", +] [[package]] name = "http" @@ -787,6 +1315,12 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "0.2.3" @@ -808,6 +1342,16 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "if-addrs" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbc0fa01ffc752e9dbc72818cdb072cd028b86be5e09dd04c5a643704fe101a9" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "indexmap" version = "1.9.2" @@ -860,6 +1404,15 @@ dependencies = [ "serde_json", ] +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -889,6 +1442,59 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", + "value-bag", +] + +[[package]] +name = "log-derive" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a42526bb432bcd1b43571d5f163984effa25409a29f1a3242a54d0577d55bcf" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "magic-wormhole" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "803ddb5f8d832299be44336b5b2ddf1c10952999498247dd516b5757be2b51c7" +dependencies = [ + "async-io", + "async-std", + "async-tar", + "async-trait", + "async-tungstenite", + "base64 0.20.0", + "bytecodec", + "derive_more", + "futures", + "futures_ringbuf", + "hex", + "hkdf", + "if-addrs", + "libc", + "log", + "noise-protocol", + "noise-rust-crypto", + "percent-encoding", + "rand 0.8.5", + "rmp-serde", + "serde", + "serde_derive", + "serde_json", + "sha-1", + "sha2", + "socket2", + "spake2", + "stun_codec", + "thiserror", + "time 0.3.18", + "url", + "xsalsa20poly1305", ] [[package]] @@ -903,9 +1509,15 @@ version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" dependencies = [ - "digest", + "digest 0.10.6", ] +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + [[package]] name = "memchr" version = "2.5.0" @@ -985,6 +1597,31 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +[[package]] +name = "noise-protocol" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb474d36dfe51bb4d7e733fee2b0dfd92ee1b95c716030a70e92737dea1a52b" +dependencies = [ + "arrayvec 0.7.2", +] + +[[package]] +name = "noise-rust-crypto" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e7cfeb8e6a63b4a5ccef34ed7a22d084a129b1e53a000c080bbc54c0da6f8c" +dependencies = [ + "aes-gcm", + "blake2", + "chacha20poly1305", + "getrandom 0.2.8", + "noise-protocol", + "sha2", + "x25519-dalek", + "zeroize", +] + [[package]] name = "noiseexplorer_nnpsk0" version = "1.0.3" @@ -997,6 +1634,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg 1.1.0", +] + [[package]] name = "num_cpus" version = "1.15.0" @@ -1013,6 +1659,12 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + [[package]] name = "openssl" version = "0.10.45" @@ -1043,7 +1695,16 @@ dependencies = [ name = "openssl-probe" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-src" +version = "111.25.0+1.1.1t" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3173cd3626c43e3854b1b727422a276e568d9ec5fe8cec197822cf52cfb743d6" +dependencies = [ + "cc", +] [[package]] name = "openssl-sys" @@ -1054,10 +1715,17 @@ dependencies = [ "autocfg 1.1.0", "cc", "libc", + "openssl-src", "pkg-config", "vcpkg", ] +[[package]] +name = "parking" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" + [[package]] name = "parking_lot" version = "0.12.1" @@ -1081,11 +1749,17 @@ dependencies = [ "windows-sys 0.42.0", ] +[[package]] +name = "paste" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" + [[package]] name = "payjoin" version = "0.5.1-alpha" dependencies = [ - "base64", + "base64 0.13.1", "bip21", "bitcoin 0.29.2", "bitcoind", @@ -1099,9 +1773,10 @@ dependencies = [ name = "payjoin-client" version = "0.1.0" dependencies = [ - "base64", + "base64 0.13.1", "bitcoincore-rpc 0.14.0", "clap", + "magic-wormhole", "noiseexplorer_nnpsk0", "payjoin", "rand 0.6.5", @@ -1115,7 +1790,7 @@ dependencies = [ name = "payjoin-relay" version = "0.1.0" dependencies = [ - "base64", + "base64 0.13.1", "bitcoincore-rpc 0.14.0", "clap", "env_logger", @@ -1139,6 +1814,26 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3637c05577168127568a64e9dc5a6887da720efef07b3d9472d45f63ab191166" +[[package]] +name = "pin-project" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.2.9" @@ -1157,6 +1852,43 @@ version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +[[package]] +name = "polling" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22122d5ec4f9fe1b3916419b76be1e80bcb93f618d071d2edf841b137b2a2bd6" +dependencies = [ + "autocfg 1.1.0", + "cfg-if", + "libc", + "log", + "wepoll-ffi", + "windows-sys 0.42.0", +] + +[[package]] +name = "poly1305" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "polyval" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1271,13 +2003,22 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.8", ] [[package]] @@ -1392,7 +2133,7 @@ version = "0.11.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68cc60575865c7831548863cc02356512e3f1dc2f3f82cb837d7fc4cc8f3c97c" dependencies = [ - "base64", + "base64 0.13.1", "bytes", "encoding_rs", "futures-core", @@ -1438,13 +2179,53 @@ dependencies = [ "winapi", ] +[[package]] +name = "ringbuf" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f65af18d50f789e74aaf23bbb3f65dcd22a3cb6e029b5bced149f6bd57c5c2a2" +dependencies = [ + "cache-padded", +] + +[[package]] +name = "rmp" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44519172358fd6d58656c86ab8e7fbc9e1490c3e8f14d35ed78ca0dd07403c9f" +dependencies = [ + "byteorder", + "num-traits", + "paste", +] + +[[package]] +name = "rmp-serde" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5b13be192e0220b8afb7222aa5813cb62cc269ebb5cac346ca6487681d2913e" +dependencies = [ + "byteorder", + "rmp", + "serde", +] + [[package]] name = "rustc_version" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" dependencies = [ - "semver", + "semver 0.9.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver 1.0.16", ] [[package]] @@ -1453,7 +2234,7 @@ version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" dependencies = [ - "base64", + "base64 0.13.1", "log", "ring", "sct", @@ -1466,6 +2247,16 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" +[[package]] +name = "salsa20" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0fbb5f676da676c260ba276a8f43a8dc67cf02d1438423aeb1c677a7212686" +dependencies = [ + "cipher", + "zeroize", +] + [[package]] name = "schannel" version = "0.1.20" @@ -1563,6 +2354,12 @@ dependencies = [ "semver-parser", ] +[[package]] +name = "semver" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" + [[package]] name = "semver-parser" version = "0.7.0" @@ -1612,6 +2409,23 @@ dependencies = [ "serde", ] +[[package]] +name = "sha-1" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.6", +] + +[[package]] +name = "sha1" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c" + [[package]] name = "sha1" version = "0.6.1" @@ -1627,6 +2441,27 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.6", +] + +[[package]] +name = "signal-hook" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9" +dependencies = [ + "libc", + "signal-hook-registry", +] + [[package]] name = "signal-hook-registry" version = "1.4.0" @@ -1661,6 +2496,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "spake2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7139ade210556eca57dfc299ec2454846ab6be09232eb1139a36e285ae7fd48e" +dependencies = [ + "curve25519-dalek", + "hkdf", + "rand_core 0.5.1", + "sha2", +] + [[package]] name = "spin" version = "0.5.2" @@ -1683,7 +2530,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" dependencies = [ "discard", - "rustc_version", + "rustc_version 0.2.3", "stdweb-derive", "stdweb-internal-macros", "stdweb-internal-runtime", @@ -1715,7 +2562,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "sha1", + "sha1 0.6.1", "syn", ] @@ -1731,14 +2578,20 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +[[package]] +name = "strsim" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" + [[package]] name = "stun" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7e94b1ec00bad60e6410e058b52f1c66de3dc5fe4d62d09b3e52bb7d3b73e25" dependencies = [ - "base64", - "crc", + "base64 0.13.1", + "crc 3.0.0", "lazy_static", "md-5", "rand 0.8.5", @@ -1750,6 +2603,20 @@ dependencies = [ "webrtc-util", ] +[[package]] +name = "stun_codec" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f1df6c4e592afc1ffed8f4bc50ab9f4d70cfccb42829662b7d276247cbef3b1" +dependencies = [ + "bytecodec", + "byteorder", + "crc 2.1.0", + "hmac-sha1", + "md5", + "trackable 1.2.0", +] + [[package]] name = "subtle" version = "2.4.1" @@ -1767,6 +2634,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + [[package]] name = "tar" version = "0.4.38" @@ -1851,11 +2730,29 @@ dependencies = [ "libc", "standback", "stdweb", - "time-macros", + "time-macros 0.1.1", "version_check", "winapi", ] +[[package]] +name = "time" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af0097eaf301d576d0b2aead7a59facab6d53cc636340f0291fab8446a2e8613" +dependencies = [ + "itoa", + "serde", + "time-core", + "time-macros 0.2.6", +] + +[[package]] +name = "time-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" + [[package]] name = "time-macros" version = "0.1.1" @@ -1866,6 +2763,15 @@ dependencies = [ "time-macros-impl", ] +[[package]] +name = "time-macros" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" +dependencies = [ + "time-core", +] + [[package]] name = "time-macros-impl" version = "0.1.2" @@ -1975,12 +2881,60 @@ dependencies = [ "once_cell", ] +[[package]] +name = "trackable" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98abb9e7300b9ac902cc04920945a874c1973e08c310627cc4458c04b70dd32" +dependencies = [ + "trackable 1.2.0", + "trackable_derive", +] + +[[package]] +name = "trackable" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "017e2a1a93718e4e8386d037cfb8add78f1d690467f4350fb582f55af1203167" +dependencies = [ + "trackable_derive", +] + +[[package]] +name = "trackable_derive" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebeb235c5847e2f82cfe0f07eb971d1e5f6804b18dac2ae16349cc604380f82f" +dependencies = [ + "quote", + "syn", +] + [[package]] name = "try-lock" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +[[package]] +name = "tungstenite" +version = "0.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" +dependencies = [ + "base64 0.13.1", + "byteorder", + "bytes", + "http", + "httparse", + "log", + "rand 0.8.5", + "sha-1", + "thiserror", + "url", + "utf-8", +] + [[package]] name = "turn" version = "0.6.1" @@ -1988,7 +2942,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4712ee30d123ec7ae26d1e1b218395a16c87cdbaf4b3925d170d684af62ea5e8" dependencies = [ "async-trait", - "base64", + "base64 0.13.1", "futures", "log", "md-5", @@ -2033,6 +2987,22 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "universal-hash" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +dependencies = [ + "generic-array", + "subtle", +] + [[package]] name = "untrusted" version = "0.7.1" @@ -2045,7 +3015,7 @@ version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b8b063c2d59218ae09f22b53c42eaad0d53516457905f5235ca4bc9e99daa71" dependencies = [ - "base64", + "base64 0.13.1", "chunked_transfer", "cookie", "cookie_store", @@ -2067,6 +3037,23 @@ dependencies = [ "form_urlencoded", "idna 0.3.0", "percent-encoding", + "serde", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "value-bag" +version = "1.0.0-alpha.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2209b78d1249f7e6f3293657c9779fe31ced465df091bbd433a1cf88e916ec55" +dependencies = [ + "ctor", + "version_check", ] [[package]] @@ -2087,6 +3074,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + [[package]] name = "want" version = "0.3.0" @@ -2097,6 +3090,12 @@ dependencies = [ "try-lock", ] +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + [[package]] name = "wasi" version = "0.10.0+wasi-snapshot-preview1" @@ -2225,6 +3224,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "wepoll-ffi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" +dependencies = [ + "cc", +] + [[package]] name = "which" version = "4.3.0" @@ -2376,6 +3384,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "x25519-dalek" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2392b6b94a576b4e2bf3c5b2757d63f10ada8020a2e4d08ac849ebcf6ea8e077" +dependencies = [ + "curve25519-dalek", + "rand_core 0.5.1", + "zeroize", +] + [[package]] name = "xattr" version = "0.2.3" @@ -2385,11 +3404,40 @@ dependencies = [ "libc", ] +[[package]] +name = "xsalsa20poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e68bcb965d6c650091450b95cea12f07dcd299a01c15e2f9433b0813ea3c0886" +dependencies = [ + "aead", + "poly1305", + "rand_core 0.6.4", + "salsa20", + "subtle", + "zeroize", +] + [[package]] name = "zeroize" -version = "1.5.7" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] [[package]] name = "zip" diff --git a/payjoin-client.Dockerfile b/payjoin-client.Dockerfile new file mode 100644 index 000000000..8294a509c --- /dev/null +++ b/payjoin-client.Dockerfile @@ -0,0 +1,28 @@ +# x86_64-unknown-linux-musl + +## Initial build Stage +FROM rustlang/rust:nightly + +WORKDIR /usr/src/payjoin-client +COPY Cargo.toml Cargo.lock ./ +COPY nnpsk0/Cargo.toml ./nnpsk0/ +COPY nnpsk0/src ./nnpsk0/src/ +COPY payjoin/Cargo.toml ./payjoin/ +COPY payjoin/src ./payjoin/src/ +COPY payjoin-client/Cargo.toml ./payjoin-client/ +COPY payjoin-client/src ./payjoin-client/src/ +COPY payjoin-relay/Cargo.toml ./payjoin-relay/ +COPY payjoin-relay/src ./payjoin-relay/src/ + +# Install the required dependencies to build for `musl` static linking +RUN apt-get update && apt-get install -y musl-tools musl-dev libssl-dev +# Add our x86 target to rust, then compile and install +RUN rustup target add x86_64-unknown-linux-musl +RUN cargo build --release --bin=payjoin-client --target x86_64-unknown-linux-musl + +FROM alpine:latest +RUN apk --no-cache add ca-certificates +COPY --from=0 /usr/src/payjoin-client/target/x86_64-unknown-linux-musl/release/payjoin-client ./ +# Run +ENTRYPOINT ["./payjoin-client"] + diff --git a/payjoin-client/Cargo.toml b/payjoin-client/Cargo.toml index 2cb1c95fa..5f7ed6151 100644 --- a/payjoin-client/Cargo.toml +++ b/payjoin-client/Cargo.toml @@ -10,10 +10,11 @@ edition = "2018" payjoin = { path = "../payjoin", features = ["sender", "receiver"] } bitcoincore-rpc = "0.14.0" rand = "0.6.5" -reqwest = { version = "0.11.4", features = ["blocking"] } +reqwest = { version = "0.11.4", features = ["blocking", "native-tls-vendored"] } base64 = "0.13.0" clap = "2.33.3" turn = "0.6.1" tokio = { version = "1.6.1", features = ["full"] } webrtc-util = { version = "0.7.0", default-features = false, features = ["conn", "vnet"] } -noiseexplorer_nnpsk0 = { path = "../nnpsk0" } \ No newline at end of file +noiseexplorer_nnpsk0 = { path = "../nnpsk0" } +magic-wormhole = "0.6.0" \ No newline at end of file diff --git a/payjoin-client/README.md b/payjoin-client/README.md new file mode 100644 index 000000000..39198956d --- /dev/null +++ b/payjoin-client/README.md @@ -0,0 +1,37 @@ +# Payjoin Relay + +This is a simple TURN relay for payjoin clients to connect peer-to-peer. + +The payjoin client sends and receives payjoins for a configured bitcoin rpc client wallet. In its current form it demonstrates Serverless Payjoin capabilities by communicating over p2p UDP extablished using TURN. + +## Demo + +Follow along to see how it works + +### The relay + +This demo relies on the default rendesvous and transit relay servers operated by magic-wormhole maintainer: "lothar.com/wormhole/text-or-file-xfer + +### Start the recipient + +Connect the server to a bitcoind rpc host and specify the amount you would like to request, in sats + +```console +sudo docker run --network host --mount type=bind,source=/home/bob/.bitcoin/testnet3/,target=/testnet3/ dangould/payjoin-client:0.1.0.1 --port=18332 --cookie-file='/testnet3/.cookie' --amount=20000 +``` + +The server should output arguments for the client, e.g. + +```console +--code="4-combustion-standard" --psk="px9wvb+p38dCB1nGIHoQaECn9EXxRHxDSAecVfhqb1E=" --bip21="BITCOIN:TB1QA82L7NTU2UV0DV8UM3H204X7VPKVNY8QQCMZXU?amount=0.0002&pj=https://example.com" +``` + +### Send the payjoin + +With the sender arguments in hand, we can send from a second funded bitcoind rpc host + +```console +sudo docker run --network host --mount type=bind,source=/home/alice/.bitcoin/testnet3/,target=/testnet3/ dangould/payjoin-client:0.1.0.1 --port=18332 --cookie-file='/testnet3/.cookie' --code="4-combustion-standard" --psk="px9wvb+p38dCB1nGIHoQaECn9EXxRHxDSAecVfhqb1E=" --bip21="BITCOIN:TB1QA82L7NTU2UV0DV8UM3H204X7VPKVNY8QQCMZXU?amount=0.0002&pj=https://example.com" +``` + +When the chain advances, the new payjoin transaction should confirm. diff --git a/payjoin-client/src/main.rs b/payjoin-client/src/main.rs index cd354fc53..9c00582ec 100644 --- a/payjoin-client/src/main.rs +++ b/payjoin-client/src/main.rs @@ -18,6 +18,7 @@ use webrtc_util::Conn; use noiseexplorer_nnpsk0::noisesession::NoiseSession; use noiseexplorer_nnpsk0::consts::{MAC_LENGTH, DHLEN}; use noiseexplorer_nnpsk0::types::Keypair; +use magic_wormhole::Wormhole; #[tokio::main] @@ -46,22 +47,16 @@ async fn main() -> Result<(), Error> { .long("bip21") .help("The BIP21 URI to send to") .takes_value(true)) - .arg(Arg::with_name("endpoint") - .short("e") - .long("endpoint") - .help("The endpoint to send the payjoin to") + .arg(Arg::with_name("code") + .short("o") + .long("code") + .help("The magic wormhole code to send the payjoin to") .takes_value(true)) .arg(Arg::with_name("psk") .short("s") .long("psk") .help("The pre-shared symmetric key") .takes_value(true)) - - .arg(Arg::with_name("relay") - .short("r") - .long("relay") - .help("PayJoin TURN server relay address to establish p2p networking") - .takes_value(true)) .arg(Arg::with_name("amount") .short("a") .long("amount") @@ -84,17 +79,16 @@ async fn main() -> Result<(), Error> { ) .unwrap(); - if matches.is_present("relay") { - let relay = matches.value_of("relay").unwrap(); + if matches.is_present("amount") { let amount = matches.value_of("amount").unwrap(); let amount = Amount::from_sat(amount.parse::().unwrap()); - listen_receiver(relay, amount, bitcoind).await?; + listen_receiver(amount, bitcoind).await?; } else { let bip21 = matches.value_of("bip21").unwrap().as_ref(); - let endpoint = matches.value_of("endpoint").unwrap(); + let code = matches.value_of("code").unwrap(); let psk = matches.value_of("psk").unwrap(); let (req, ctx) = create_pj_request(bip21, &bitcoind); //base64 request - let payjoin_psbt = do_send(req, ctx, endpoint, psk).await?; + let payjoin_psbt = do_send(req, ctx, code, psk).await?; let psbt = bitcoind.wallet_process_psbt(&serialize_psbt(&payjoin_psbt), None, None, None).unwrap().psbt; let tx = bitcoind.finalize_psbt(&psbt, Some(true)).unwrap().hex.expect("incomplete psbt"); let txid = bitcoind.send_raw_transaction(&tx).unwrap(); @@ -104,57 +98,23 @@ async fn main() -> Result<(), Error> { Ok(()) } -async fn listen_receiver(relay: &str, amount: Amount, bitcoind: bitcoincore_rpc::Client) -> Result<(), Error> { - // 1. ConnectReceive - // Ensure relay connection - // TURN client won't create a local listening socket by itself. - let conn = UdpSocket::bind("0.0.0.0:0").await?; - - let turn_server_addr = relay.to_string(); - - let cfg = ClientConfig { - stun_serv_addr: turn_server_addr.clone(), - turn_serv_addr: turn_server_addr, - username: "receiver".to_string(), - password: "test".to_string(), - realm: "pj.chaincase.app".to_string(), - software: String::new(), - rto_in_ms: 0, - conn: Arc::new(conn), - vnet: None, - }; - - let client = Client::new(cfg).await?; - - // Start listening on the conn provided. - client.listen().await?; - - // Allocate a relay socket on the TURN server. On success, it - // will return a net.PacketConn which represents the remote - // socket. - let relay_conn = client.allocate().await?; - - // The relayConn's local address is actually the transport - // address assigned on the TURN server. - let relay_conn_endpoint = relay_conn.local_addr()?; +async fn listen_receiver(amount: Amount, bitcoind: bitcoincore_rpc::Client) -> Result<(), Error> { let psk = gen_psk(); - print!("--endpoint={} --psk=\"{}\" ", relay_conn_endpoint, psk); - - let mapped_addr = client.send_binding_request().await?; - // punch UDP hole. after this packets from the IP address will be accepted by the turn server - relay_conn.send_to("Hello".as_bytes(), mapped_addr).await?; - - // 2. Recv + let (welcome, res) = Wormhole::connect_without_code(magic_wormhole::transfer::APP_CONFIG, 2).await.unwrap(); + print!("--code={:?} --psk=\"{}\" ", welcome.code.0, psk); let pj_receiver_address = bitcoind.get_new_address(None, Some(bitcoincore_rpc::json::AddressType::Bech32)).unwrap(); print_payjoin_uri(pj_receiver_address, amount); - process_original_psbt(relay_conn, bitcoind, psk).await?; - client.close().await?; + // 2. Recv + let mut wormhole = res.await.unwrap(); + process_original_psbt(&mut wormhole, bitcoind, psk).await?; + + wormhole.close().await.unwrap(); Ok(()) } async fn process_original_psbt( - relay_conn: impl Conn + std::marker::Send + std::marker::Sync + 'static, + wormhole: &mut Wormhole, receiver: bitcoincore_rpc::Client, psk: String, ) -> Result<(), Error> { @@ -166,14 +126,13 @@ async fn process_original_psbt( let psk: [u8; 32] = base64::decode(psk).unwrap().try_into().unwrap(); let psk = noiseexplorer_nnpsk0::types::Psk::from_bytes(psk); - - let mut buf = [0u8; 1024]; - let (n, from) = relay_conn.recv_from(&mut buf).await?; - println!("received {} bytes of supposed Original PSBT from {}", n, from); - println!("buf: {:?}", &buf[..n]); + + let mut buf = wormhole.receive().await.unwrap(); + println!("received {} bytes of supposed Original PSBT", buf.len()); + // security does not depend on long-term static keys in NNpsk0. The interface still requires a Keypair, but it is not used. let mut responder = NoiseSession::init_session(false, b"", Keypair::new_empty(), psk); - responder.recv_message(&mut buf[..n]).unwrap(); // es derived internally + responder.recv_message(&mut buf).unwrap(); // es derived internally let (_initiator_e, payload) = buf.split_at_mut(DHLEN); let (payload, _mac) = payload.split_at_mut(payload.len() - MAC_LENGTH); let n = payload.len(); // hopefully from_request can trim the padding, else we're gonna need to send the length on the wire as headers does @@ -236,17 +195,16 @@ async fn process_original_psbt( in_out.resize(message_b_size, 0); responder.send_message(&mut in_out).unwrap(); // e, ee - relay_conn.send_to(in_out.as_slice(), from).await?; + wormhole.send(in_out).await.unwrap(); Ok(()) } -async fn do_send(req: payjoin::sender::Request, ctx: payjoin::sender::Context, relay_addr: &str, psk: &str) -> Result { +async fn do_send(req: payjoin::sender::Request, ctx: payjoin::sender::Context, code: &str, psk: &str) -> Result { let mut original_psbt = req.body.clone(); //format!("{:?}", tokio::time::Instant::now()); - // Set up pinger socket (pingerConn) - let pinger_conn_tx = Arc::new(UdpSocket::bind("0.0.0.0:0").await?); - let pinger_conn_rx = Arc::clone(&pinger_conn_tx); + // Set up wormhole + let (_, mut wormhole) = Wormhole::connect_with_code(magic_wormhole::transfer::APP_CONFIG, magic_wormhole::Code(code.to_owned())).await.unwrap(); // do noise secure handshake let psk: [u8; 32] = base64::decode(psk).unwrap().try_into().unwrap(); @@ -262,21 +220,18 @@ async fn do_send(req: payjoin::sender::Request, ctx: payjoin::sender::Context, r println!("sending in_out: {:?}", in_out); initiator.send_message(&mut in_out).unwrap(); // psk, e println!("sending message_a: {:?}", in_out); - pinger_conn_tx.send_to(in_out.as_slice(), relay_addr).await?; + wormhole.send(in_out).await.unwrap(); - - println!("len: {} < 1024?", req.body.len()); - let mut buf = [0u8; 1024]; - let (n, from) = pinger_conn_rx.recv_from(&mut buf).await?; + let mut buf = wormhole.receive().await.unwrap(); // finish noise handshake - initiator.recv_message(&mut buf[..n]).unwrap(); + initiator.recv_message(&mut buf).unwrap(); let (_responder_e, payload) = buf.split_at_mut(DHLEN); let (payload, _mac) = payload.split_at_mut(payload.len() - MAC_LENGTH); let n = payload.len(); let proposal = ctx.process_response(&payload[..n]).unwrap(); - println!("proposal: {:#?} from {}", proposal, from); + println!("proposal: {:#?}", proposal); Ok(proposal) } diff --git a/payjoin-relay.Dockerfile b/payjoin-relay.Dockerfile new file mode 100644 index 000000000..a6d557d46 --- /dev/null +++ b/payjoin-relay.Dockerfile @@ -0,0 +1,28 @@ +# x86_64-unknown-linux-musl + +## Initial build Stage +FROM rustlang/rust:nightly + +WORKDIR /usr/src/payjoin-relay +COPY Cargo.toml Cargo.lock ./ +COPY nnpsk0/Cargo.toml ./nnpsk0/ +COPY nnpsk0/src ./nnpsk0/src/ +COPY payjoin/Cargo.toml ./payjoin/ +COPY payjoin/src ./payjoin/src/ +COPY payjoin-client/Cargo.toml ./payjoin-client/ +COPY payjoin-client/src ./payjoin-client/src/ +COPY payjoin-relay/Cargo.toml ./payjoin-relay/ +COPY payjoin-relay/src ./payjoin-relay/src/ + +# Install the required dependencies to build for `musl` static linking +RUN apt-get update && apt-get install -y musl-tools musl-dev libssl-dev +# Add our x86 target to rust, then compile and install +RUN rustup target add x86_64-unknown-linux-musl +RUN cargo build --release --bin=payjoin-relay --target x86_64-unknown-linux-musl + +FROM alpine:latest +RUN apk --no-cache add ca-certificates +COPY --from=0 /usr/src/payjoin-relay/target/x86_64-unknown-linux-musl/release/payjoin-relay ./ +# Run +ENTRYPOINT ["./payjoin-relay"] + diff --git a/payjoin-relay/Cargo.toml b/payjoin-relay/Cargo.toml index a0976bfa8..c6a076eca 100644 --- a/payjoin-relay/Cargo.toml +++ b/payjoin-relay/Cargo.toml @@ -9,7 +9,7 @@ edition = "2018" [dependencies] payjoin = { path = "../payjoin", features = ["sender"] } bitcoincore-rpc = "0.14.0" -reqwest = { version = "0.11.4", features = ["blocking"] } +reqwest = { version = "0.11.4", features = ["blocking", "native-tls-vendored"] } base64 = "0.13.0" turn = "0.6.1" webrtc-util = { version = "0.7.0", default-features = false, features = ["conn", "vnet"] } diff --git a/payjoin-relay/README.md b/payjoin-relay/README.md deleted file mode 100644 index d756f7d55..000000000 --- a/payjoin-relay/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# Payjoin Relay - -This is a simple TURN relay for payjoin clients to connect peer-to-peer. - -The payjoin client sends and receives payjoins for a configured bitcoin rpc client wallet. In its current form it demonstrates Serverless Payjoin capabilities by communicating over p2p UDP extablished using TURN. - -## Demo - -Follow along to see how it works - -### Start a relay - -This relay has permission for a "receiver" user to request an allocation with password "test" - -```console -RUST_LOG=trace cargo run --bin payjoin-relay -- --public-ip 0.0.0.0 --users receiver=test -``` - -### Start the recipient - -Connect the server to a bitcoind rpc host and specify the amount you would like to request, in sats - -```console -RUST_LOG=trace cargo run --bin payjoin-client -- --port=18444 --cookie-file="/Users/dan/.polar/networks/1/volumes/bitcoind/backend2/regtest/.cookie" --amount=200000 --relay=0.0.0.0:3478 -``` - -The server should output arguments for the client - -```console ---endpoint=0.0.0.0:61629 --psk="EFA92zD3PjUlUg5uX/rsQ/+SFkJi3V3HGKi3H6WWw8I=" --bip21="BITCOIN:BCRT1QG8ZWJWLT640CJ076VLTDLKS2GJCF596XLYER95?amount=0.002&pj=https://example.com" -``` - -### Send the payjoin - -With the sender arguments in hand, we can send from a second funded bitcoind rpc host - -```console -RUST_LOG=trace cargo run --bin payjoin-client -- --port=18443 --cookie-file="/Users/dan/.polar/networks/1/volumes/bitcoind/backend1/regtest/.cookie" --endpoint=0.0.0.0:61629 --psk="EFA92zD3PjUlUg5uX/rsQ/+SFkJi3V3HGKi3H6WWw8I=" --bip21="BITCOIN:BCRT1QG8ZWJWLT640CJ076VLTDLKS2GJCF596XLYER95?amount=0.002&pj=https://example.com" -``` - -When the chain advances, the new payjoin transaction should confirm. From 90ea09a96d886afc04ad19af467f95196175b0d6 Mon Sep 17 00:00:00 2001 From: DanGould Date: Tue, 21 Feb 2023 13:26:23 -0500 Subject: [PATCH 24/24] Relay payjoin with http only --- Cargo.lock | 1375 +++----------------------- payjoin-client/Cargo.toml | 4 +- payjoin-client/src/http_relay/mod.rs | 109 ++ payjoin-client/src/main.rs | 59 +- 4 files changed, 298 insertions(+), 1249 deletions(-) create mode 100644 payjoin-client/src/http_relay/mod.rs diff --git a/Cargo.lock b/Cargo.lock index e776b9b02..ac638835f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,42 +8,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "aead" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" -dependencies = [ - "generic-array", - "rand_core 0.6.4", -] - -[[package]] -name = "aes" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", - "opaque-debug", -] - -[[package]] -name = "aes-gcm" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6" -dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", - "subtle", -] - [[package]] name = "aho-corasick" version = "0.7.20" @@ -71,203 +35,17 @@ dependencies = [ "nodrop", ] -[[package]] -name = "arrayvec" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" - -[[package]] -name = "async-attributes" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" -dependencies = [ - "quote", - "syn", -] - -[[package]] -name = "async-channel" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833" -dependencies = [ - "concurrent-queue", - "event-listener", - "futures-core", -] - -[[package]] -name = "async-executor" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17adb73da160dfb475c183343c8cccd80721ea5a605d3eb57125f0a7b7a92d0b" -dependencies = [ - "async-lock", - "async-task", - "concurrent-queue", - "fastrand", - "futures-lite", - "slab", -] - -[[package]] -name = "async-global-executor" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" -dependencies = [ - "async-channel", - "async-executor", - "async-io", - "async-lock", - "blocking", - "futures-lite", - "once_cell", -] - -[[package]] -name = "async-io" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c374dda1ed3e7d8f0d9ba58715f924862c63eae6849c92d3a18e7fbde9e2794" -dependencies = [ - "async-lock", - "autocfg 1.1.0", - "concurrent-queue", - "futures-lite", - "libc", - "log", - "parking", - "polling", - "slab", - "socket2", - "waker-fn", - "windows-sys 0.42.0", -] - -[[package]] -name = "async-lock" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8101efe8695a6c17e02911402145357e718ac92d3ff88ae8419e84b1707b685" -dependencies = [ - "event-listener", - "futures-lite", -] - -[[package]] -name = "async-process" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6381ead98388605d0d9ff86371043b5aa922a3905824244de40dc263a14fcba4" -dependencies = [ - "async-io", - "async-lock", - "autocfg 1.1.0", - "blocking", - "cfg-if", - "event-listener", - "futures-lite", - "libc", - "signal-hook", - "windows-sys 0.42.0", -] - -[[package]] -name = "async-std" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" -dependencies = [ - "async-attributes", - "async-channel", - "async-global-executor", - "async-io", - "async-lock", - "async-process", - "crossbeam-utils", - "futures-channel", - "futures-core", - "futures-io", - "futures-lite", - "gloo-timers", - "kv-log-macro", - "log", - "memchr", - "once_cell", - "pin-project-lite", - "pin-utils", - "slab", - "wasm-bindgen-futures", -] - -[[package]] -name = "async-tar" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c49359998a76e32ef6e870dbc079ebad8f1e53e8441c5dd39d27b44493fe331" -dependencies = [ - "async-std", - "filetime", - "libc", - "pin-project", - "redox_syscall", - "xattr", -] - -[[package]] -name = "async-task" -version = "4.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" - -[[package]] -name = "async-tls" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f23d769dbf1838d5df5156e7b1ad404f4c463d1ac2c6aeb6cd943630f8a8400" -dependencies = [ - "futures-core", - "futures-io", - "rustls", - "webpki", - "webpki-roots", -] - [[package]] name = "async-trait" -version = "0.1.61" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "705339e0e4a9690e2908d2b3d049d85682cf19fbd5782494498fbf7003a6a282" +checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" dependencies = [ "proc-macro2", "quote", "syn", ] -[[package]] -name = "async-tungstenite" -version = "0.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1b71b31561643aa8e7df3effe284fa83ab1a840e52294c5f4bd7bfd8b2becbb" -dependencies = [ - "async-std", - "async-tls", - "futures-io", - "futures-util", - "log", - "pin-project-lite", - "tungstenite", -] - -[[package]] -name = "atomic-waker" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "debc29dde2e69f9e47506b525f639ed42300fc014a3e007832592448fa8e4599" - [[package]] name = "atty" version = "0.2.14" @@ -308,9 +86,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" [[package]] name = "base64-compat" @@ -363,7 +141,7 @@ checksum = "0694ea59225b0c5f3cb405ff3f670e4828358ed26aec49dc352f730f0cb1a8a3" dependencies = [ "bech32 0.9.1", "bitcoin_hashes 0.11.0", - "secp256k1 0.24.2", + "secp256k1 0.24.3", "serde", ] @@ -457,22 +235,13 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "blake2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" -dependencies = [ - "digest 0.10.6", -] - [[package]] name = "blake2-rfc" version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" dependencies = [ - "arrayvec 0.4.12", + "arrayvec", "constant_time_eq", ] @@ -485,35 +254,11 @@ dependencies = [ "generic-array", ] -[[package]] -name = "blocking" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c67b173a56acffd6d2326fb7ab938ba0b00a71480e14902b2591c87bc5741e8" -dependencies = [ - "async-channel", - "async-lock", - "async-task", - "atomic-waker", - "fastrand", - "futures-lite", -] - [[package]] name = "bumpalo" -version = "3.11.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" - -[[package]] -name = "bytecodec" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adf4c9d0bbf32eea58d7c0f812058138ee8edaf0f2802b6d03561b504729a325" -dependencies = [ - "byteorder", - "trackable 0.2.24", -] +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "byteorder" @@ -523,15 +268,15 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "bzip2" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6afcd980b5f3a45017c57e57a2fcccbb351cc43a356ce117ef760ef8052b89b0" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" dependencies = [ "bzip2-sys", "libc", @@ -548,17 +293,11 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "cache-padded" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" - [[package]] name = "cc" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg-if" @@ -566,46 +305,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "chacha20" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c80e5460aa66fe3b91d40bcbdab953a597b60053e34d684ac6903f863b680a6" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", - "zeroize", -] - -[[package]] -name = "chacha20poly1305" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18446b09be63d457bbec447509e85f662f32952b035ce892290396bc0b0cff5" -dependencies = [ - "aead", - "chacha20", - "cipher", - "poly1305", - "zeroize", -] - [[package]] name = "chunked_transfer" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cca491388666e04d7248af3f60f0c40cfb0991c72205595d7c396e3510207d1a" -[[package]] -name = "cipher" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" -dependencies = [ - "generic-array", -] - [[package]] name = "clap" version = "2.34.0" @@ -615,7 +320,7 @@ dependencies = [ "ansi_term", "atty", "bitflags", - "strsim 0.8.0", + "strsim", "textwrap", "unicode-width", "vec_map", @@ -630,15 +335,6 @@ dependencies = [ "bitflags", ] -[[package]] -name = "concurrent-queue" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c278839b831783b70278b14df4d45e1beb1aad306c07bb796637de9a0e323e8e" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "const_fn" version = "0.4.9" @@ -694,39 +390,15 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" -[[package]] -name = "cpufeatures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" -dependencies = [ - "libc", -] - -[[package]] -name = "crc" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49fc9a695bca7f35f5f4c15cddc84415f66a74ea78eef08e90c5024f2b540e23" -dependencies = [ - "crc-catalog 1.1.1", -] - [[package]] name = "crc" -version = "3.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53757d12b596c16c78b83458d732a5d1a17ab3f53f2f7412f6fb57cc8a140ab3" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" dependencies = [ - "crc-catalog 2.2.0", + "crc-catalog", ] -[[package]] -name = "crc-catalog" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccaeedb56da03b09f598226e25e80088cb4cd25f316e6e4df7d695f0feeb1403" - [[package]] name = "crc-catalog" version = "2.2.0" @@ -742,15 +414,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "crossbeam-utils" -version = "0.8.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" -dependencies = [ - "cfg-if", -] - [[package]] name = "crypto-common" version = "0.1.6" @@ -761,93 +424,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "ctor" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" -dependencies = [ - "quote", - "syn", -] - -[[package]] -name = "ctr" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" -dependencies = [ - "cipher", -] - -[[package]] -name = "curve25519-dalek" -version = "3.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f9d052967f590a76e62eb387bd0bbb1b000182c3cefe5364db6b7211651bc0" -dependencies = [ - "byteorder", - "digest 0.9.0", - "rand_core 0.5.1", - "subtle", - "zeroize", -] - -[[package]] -name = "darling" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim 0.9.3", - "syn", -] - -[[package]] -name = "darling_macro" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" -dependencies = [ - "darling_core", - "quote", - "syn", -] - -[[package]] -name = "derive_more" -version = "0.99.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - [[package]] name = "digest" version = "0.10.6" @@ -856,7 +432,6 @@ checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ "block-buffer", "crypto-common", - "subtle", ] [[package]] @@ -867,15 +442,15 @@ checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" [[package]] name = "either" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "encoding_rs" -version = "0.8.31" +version = "0.8.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" dependencies = [ "cfg-if", ] @@ -893,17 +468,11 @@ dependencies = [ "termcolor", ] -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - [[package]] name = "fastrand" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] @@ -970,9 +539,9 @@ checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] name = "futures" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" +checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" dependencies = [ "futures-channel", "futures-core", @@ -985,9 +554,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" dependencies = [ "futures-core", "futures-sink", @@ -995,15 +564,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" +checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" [[package]] name = "futures-executor" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" +checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" dependencies = [ "futures-core", "futures-task", @@ -1012,30 +581,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" - -[[package]] -name = "futures-lite" -version = "1.12.0" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] +checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" [[package]] name = "futures-macro" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" +checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" dependencies = [ "proc-macro2", "quote", @@ -1044,21 +598,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" +checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" [[package]] name = "futures-task" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" +checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" [[package]] name = "futures-util" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" dependencies = [ "futures-channel", "futures-core", @@ -1072,19 +626,6 @@ dependencies = [ "slab", ] -[[package]] -name = "futures_ringbuf" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b905098b5519bd63b2a1f9f4615198b0e38a473ce201ffdbd4dea6eb63087ddc" -dependencies = [ - "futures", - "log", - "log-derive", - "ringbuf", - "rustc_version 0.4.0", -] - [[package]] name = "generic-array" version = "0.14.6" @@ -1095,17 +636,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - [[package]] name = "getrandom" version = "0.2.8" @@ -1117,28 +647,6 @@ dependencies = [ "wasi 0.11.0+wasi-snapshot-preview1", ] -[[package]] -name = "ghash" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" -dependencies = [ - "opaque-debug", - "polyval", -] - -[[package]] -name = "gloo-timers" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - [[package]] name = "h2" version = "0.3.15" @@ -1207,42 +715,12 @@ name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -dependencies = [ - "serde", -] - -[[package]] -name = "hkdf" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" -dependencies = [ - "hmac", -] - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest 0.10.6", -] - -[[package]] -name = "hmac-sha1" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1333fad8d94b82cab989da428b0b36a3435db3870d85e971a1d6dc0a8576722" -dependencies = [ - "sha1 0.2.0", -] [[package]] name = "http" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", @@ -1280,9 +758,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.23" +version = "0.14.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" +checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" dependencies = [ "bytes", "futures-channel", @@ -1315,12 +793,6 @@ dependencies = [ "tokio-native-tls", ] -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - [[package]] name = "idna" version = "0.2.3" @@ -1342,16 +814,6 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "if-addrs" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc0fa01ffc752e9dbc72818cdb072cd028b86be5e09dd04c5a643704fe101a9" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "indexmap" version = "1.9.2" @@ -1373,9 +835,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.7.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11b0d96e660696543b251e58030cf9787df56da39dab19ad60eae7353040917e" +checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" [[package]] name = "itoa" @@ -1385,9 +847,9 @@ checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" dependencies = [ "wasm-bindgen", ] @@ -1404,15 +866,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "kv-log-macro" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" -dependencies = [ - "log", -] - [[package]] name = "lazy_static" version = "1.4.0" @@ -1442,66 +895,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", - "value-bag", -] - -[[package]] -name = "log-derive" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a42526bb432bcd1b43571d5f163984effa25409a29f1a3242a54d0577d55bcf" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "magic-wormhole" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "803ddb5f8d832299be44336b5b2ddf1c10952999498247dd516b5757be2b51c7" -dependencies = [ - "async-io", - "async-std", - "async-tar", - "async-trait", - "async-tungstenite", - "base64 0.20.0", - "bytecodec", - "derive_more", - "futures", - "futures_ringbuf", - "hex", - "hkdf", - "if-addrs", - "libc", - "log", - "noise-protocol", - "noise-rust-crypto", - "percent-encoding", - "rand 0.8.5", - "rmp-serde", - "serde", - "serde_derive", - "serde_json", - "sha-1", - "sha2", - "socket2", - "spake2", - "stun_codec", - "thiserror", - "time 0.3.18", - "url", - "xsalsa20poly1305", ] [[package]] name = "matches" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "md-5" @@ -1509,15 +909,9 @@ version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" dependencies = [ - "digest 0.10.6", + "digest", ] -[[package]] -name = "md5" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" - [[package]] name = "memchr" version = "2.5.0" @@ -1551,14 +945,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -1597,31 +991,6 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" -[[package]] -name = "noise-protocol" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb474d36dfe51bb4d7e733fee2b0dfd92ee1b95c716030a70e92737dea1a52b" -dependencies = [ - "arrayvec 0.7.2", -] - -[[package]] -name = "noise-rust-crypto" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e7cfeb8e6a63b4a5ccef34ed7a22d084a129b1e53a000c080bbc54c0da6f8c" -dependencies = [ - "aes-gcm", - "blake2", - "chacha20poly1305", - "getrandom 0.2.8", - "noise-protocol", - "sha2", - "x25519-dalek", - "zeroize", -] - [[package]] name = "noiseexplorer_nnpsk0" version = "1.0.3" @@ -1634,15 +1003,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg 1.1.0", -] - [[package]] name = "num_cpus" version = "1.15.0" @@ -1655,15 +1015,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" - -[[package]] -name = "opaque-debug" -version = "0.3.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "openssl" @@ -1720,12 +1074,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "parking" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" - [[package]] name = "parking_lot" version = "0.12.1" @@ -1738,23 +1086,17 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.6" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] -[[package]] -name = "paste" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" - [[package]] name = "payjoin" version = "0.5.1-alpha" @@ -1776,13 +1118,13 @@ dependencies = [ "base64 0.13.1", "bitcoincore-rpc 0.14.0", "clap", - "magic-wormhole", + "hyper", "noiseexplorer_nnpsk0", "payjoin", "rand 0.6.5", "reqwest", "tokio", - "turn", + "url", "webrtc-util", ] @@ -1814,26 +1156,6 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3637c05577168127568a64e9dc5a6887da720efef07b3d9472d45f63ab191166" -[[package]] -name = "pin-project" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "pin-project-lite" version = "0.2.9" @@ -1852,43 +1174,6 @@ version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" -[[package]] -name = "polling" -version = "2.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22122d5ec4f9fe1b3916419b76be1e80bcb93f618d071d2edf841b137b2a2bd6" -dependencies = [ - "autocfg 1.1.0", - "cfg-if", - "libc", - "log", - "wepoll-ffi", - "windows-sys 0.42.0", -] - -[[package]] -name = "poly1305" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede" -dependencies = [ - "cpufeatures", - "opaque-debug", - "universal-hash", -] - -[[package]] -name = "polyval" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" -dependencies = [ - "cfg-if", - "cpufeatures", - "opaque-debug", - "universal-hash", -] - [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1903,9 +1188,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.49" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" dependencies = [ "unicode-ident", ] @@ -2003,22 +1288,13 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] - [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.8", + "getrandom", ] [[package]] @@ -2103,9 +1379,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ "aho-corasick", "memchr", @@ -2129,11 +1405,11 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.13" +version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68cc60575865c7831548863cc02356512e3f1dc2f3f82cb837d7fc4cc8f3c97c" +checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" dependencies = [ - "base64 0.13.1", + "base64 0.21.0", "bytes", "encoding_rs", "futures-core", @@ -2179,53 +1455,13 @@ dependencies = [ "winapi", ] -[[package]] -name = "ringbuf" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f65af18d50f789e74aaf23bbb3f65dcd22a3cb6e029b5bced149f6bd57c5c2a2" -dependencies = [ - "cache-padded", -] - -[[package]] -name = "rmp" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44519172358fd6d58656c86ab8e7fbc9e1490c3e8f14d35ed78ca0dd07403c9f" -dependencies = [ - "byteorder", - "num-traits", - "paste", -] - -[[package]] -name = "rmp-serde" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5b13be192e0220b8afb7222aa5813cb62cc269ebb5cac346ca6487681d2913e" -dependencies = [ - "byteorder", - "rmp", - "serde", -] - [[package]] name = "rustc_version" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" dependencies = [ - "semver 0.9.0", -] - -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver 1.0.16", + "semver", ] [[package]] @@ -2247,24 +1483,13 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" -[[package]] -name = "salsa20" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0fbb5f676da676c260ba276a8f43a8dc67cf02d1438423aeb1c677a7212686" -dependencies = [ - "cipher", - "zeroize", -] - [[package]] name = "schannel" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" dependencies = [ - "lazy_static", - "windows-sys 0.36.1", + "windows-sys 0.42.0", ] [[package]] @@ -2295,9 +1520,9 @@ dependencies = [ [[package]] name = "secp256k1" -version = "0.24.2" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9512ffd81e3a3503ed401f79c33168b9148c75038956039166cd750eaa037c3" +checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62" dependencies = [ "bitcoin_hashes 0.11.0", "secp256k1-sys 0.6.1", @@ -2324,9 +1549,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.7.0" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" dependencies = [ "bitflags", "core-foundation", @@ -2337,9 +1562,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.6.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" dependencies = [ "core-foundation-sys", "libc", @@ -2354,12 +1579,6 @@ dependencies = [ "semver-parser", ] -[[package]] -name = "semver" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" - [[package]] name = "semver-parser" version = "0.7.0" @@ -2388,9 +1607,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.91" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" +checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" dependencies = [ "itoa", "ryu", @@ -2409,23 +1628,6 @@ dependencies = [ "serde", ] -[[package]] -name = "sha-1" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.6", -] - -[[package]] -name = "sha1" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c" - [[package]] name = "sha1" version = "0.6.1" @@ -2441,41 +1643,20 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" -[[package]] -name = "sha2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.6", -] - -[[package]] -name = "signal-hook" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9" -dependencies = [ - "libc", - "signal-hook-registry", -] - [[package]] name = "signal-hook-registry" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] [[package]] name = "slab" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg 1.1.0", ] @@ -2496,18 +1677,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "spake2" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7139ade210556eca57dfc299ec2454846ab6be09232eb1139a36e285ae7fd48e" -dependencies = [ - "curve25519-dalek", - "hkdf", - "rand_core 0.5.1", - "sha2", -] - [[package]] name = "spin" version = "0.5.2" @@ -2530,7 +1699,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" dependencies = [ "discard", - "rustc_version 0.2.3", + "rustc_version", "stdweb-derive", "stdweb-internal-macros", "stdweb-internal-runtime", @@ -2562,7 +1731,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "sha1 0.6.1", + "sha1", "syn", ] @@ -2578,12 +1747,6 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" -[[package]] -name = "strsim" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" - [[package]] name = "stun" version = "0.4.4" @@ -2591,7 +1754,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7e94b1ec00bad60e6410e058b52f1c66de3dc5fe4d62d09b3e52bb7d3b73e25" dependencies = [ "base64 0.13.1", - "crc 3.0.0", + "crc", "lazy_static", "md-5", "rand 0.8.5", @@ -2603,20 +1766,6 @@ dependencies = [ "webrtc-util", ] -[[package]] -name = "stun_codec" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f1df6c4e592afc1ffed8f4bc50ab9f4d70cfccb42829662b7d276247cbef3b1" -dependencies = [ - "bytecodec", - "byteorder", - "crc 2.1.0", - "hmac-sha1", - "md5", - "trackable 1.2.0", -] - [[package]] name = "subtle" version = "2.4.1" @@ -2634,18 +1783,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "unicode-xid", -] - [[package]] name = "tar" version = "0.4.38" @@ -2673,9 +1810,9 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] @@ -2730,29 +1867,11 @@ dependencies = [ "libc", "standback", "stdweb", - "time-macros 0.1.1", + "time-macros", "version_check", "winapi", ] -[[package]] -name = "time" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af0097eaf301d576d0b2aead7a59facab6d53cc636340f0291fab8446a2e8613" -dependencies = [ - "itoa", - "serde", - "time-core", - "time-macros 0.2.6", -] - -[[package]] -name = "time-core" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" - [[package]] name = "time-macros" version = "0.1.1" @@ -2763,15 +1882,6 @@ dependencies = [ "time-macros-impl", ] -[[package]] -name = "time-macros" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" -dependencies = [ - "time-core", -] - [[package]] name = "time-macros-impl" version = "0.1.2" @@ -2796,15 +1906,15 @@ dependencies = [ [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.23.0" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46" +checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" dependencies = [ "autocfg 1.1.0", "bytes", @@ -2833,9 +1943,9 @@ dependencies = [ [[package]] name = "tokio-native-tls" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", "tokio", @@ -2843,9 +1953,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.4" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" dependencies = [ "bytes", "futures-core", @@ -2881,59 +1991,11 @@ dependencies = [ "once_cell", ] -[[package]] -name = "trackable" -version = "0.2.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98abb9e7300b9ac902cc04920945a874c1973e08c310627cc4458c04b70dd32" -dependencies = [ - "trackable 1.2.0", - "trackable_derive", -] - -[[package]] -name = "trackable" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "017e2a1a93718e4e8386d037cfb8add78f1d690467f4350fb582f55af1203167" -dependencies = [ - "trackable_derive", -] - -[[package]] -name = "trackable_derive" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebeb235c5847e2f82cfe0f07eb971d1e5f6804b18dac2ae16349cc604380f82f" -dependencies = [ - "quote", - "syn", -] - [[package]] name = "try-lock" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" - -[[package]] -name = "tungstenite" -version = "0.17.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" -dependencies = [ - "base64 0.13.1", - "byteorder", - "bytes", - "http", - "httparse", - "log", - "rand 0.8.5", - "sha-1", - "thiserror", - "url", - "utf-8", -] +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "turn" @@ -2962,9 +2024,9 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "unicode-bidi" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" [[package]] name = "unicode-ident" @@ -2987,22 +2049,6 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - -[[package]] -name = "universal-hash" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" -dependencies = [ - "generic-array", - "subtle", -] - [[package]] name = "untrusted" version = "0.7.1" @@ -3037,23 +2083,6 @@ dependencies = [ "form_urlencoded", "idna 0.3.0", "percent-encoding", - "serde", -] - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "value-bag" -version = "1.0.0-alpha.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2209b78d1249f7e6f3293657c9779fe31ced465df091bbd433a1cf88e916ec55" -dependencies = [ - "ctor", - "version_check", ] [[package]] @@ -3074,12 +2103,6 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "waker-fn" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" - [[package]] name = "want" version = "0.3.0" @@ -3090,12 +2113,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - [[package]] name = "wasi" version = "0.10.0+wasi-snapshot-preview1" @@ -3110,9 +2127,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -3120,9 +2137,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" dependencies = [ "bumpalo", "log", @@ -3135,9 +2152,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" dependencies = [ "cfg-if", "js-sys", @@ -3147,9 +2164,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3157,9 +2174,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", @@ -3170,15 +2187,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" dependencies = [ "js-sys", "wasm-bindgen", @@ -3224,20 +2241,11 @@ dependencies = [ "winapi", ] -[[package]] -name = "wepoll-ffi" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" -dependencies = [ - "cc", -] - [[package]] name = "which" -version = "4.3.0" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b" +checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" dependencies = [ "either", "libc", @@ -3275,19 +2283,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" -dependencies = [ - "windows_aarch64_msvc 0.36.1", - "windows_i686_gnu 0.36.1", - "windows_i686_msvc 0.36.1", - "windows_x86_64_gnu 0.36.1", - "windows_x86_64_msvc 0.36.1", -] - [[package]] name = "windows-sys" version = "0.42.0" @@ -3295,85 +2290,79 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ "windows_aarch64_gnullvm", - "windows_aarch64_msvc 0.42.0", - "windows_i686_gnu 0.42.0", - "windows_i686_msvc 0.42.0", - "windows_x86_64_gnu 0.42.0", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", "windows_x86_64_gnullvm", - "windows_x86_64_msvc 0.42.0", + "windows_x86_64_msvc", ] [[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.0" +name = "windows-sys" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] [[package]] -name = "windows_aarch64_msvc" -version = "0.36.1" +name = "windows-targets" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] [[package]] -name = "windows_aarch64_msvc" -version = "0.42.0" +name = "windows_aarch64_gnullvm" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" [[package]] -name = "windows_i686_gnu" -version = "0.36.1" +name = "windows_aarch64_msvc" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" [[package]] name = "windows_i686_gnu" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" - -[[package]] -name = "windows_i686_msvc" -version = "0.36.1" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" [[package]] name = "windows_i686_msvc" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.36.1" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" [[package]] name = "windows_x86_64_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" [[package]] name = "windows_x86_64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] name = "winreg" @@ -3384,17 +2373,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "x25519-dalek" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2392b6b94a576b4e2bf3c5b2757d63f10ada8020a2e4d08ac849ebcf6ea8e077" -dependencies = [ - "curve25519-dalek", - "rand_core 0.5.1", - "zeroize", -] - [[package]] name = "xattr" version = "0.2.3" @@ -3404,40 +2382,11 @@ dependencies = [ "libc", ] -[[package]] -name = "xsalsa20poly1305" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e68bcb965d6c650091450b95cea12f07dcd299a01c15e2f9433b0813ea3c0886" -dependencies = [ - "aead", - "poly1305", - "rand_core 0.6.4", - "salsa20", - "subtle", - "zeroize", -] - [[package]] name = "zeroize" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.3.3" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" [[package]] name = "zip" diff --git a/payjoin-client/Cargo.toml b/payjoin-client/Cargo.toml index 5f7ed6151..0b8f6816b 100644 --- a/payjoin-client/Cargo.toml +++ b/payjoin-client/Cargo.toml @@ -11,10 +11,10 @@ payjoin = { path = "../payjoin", features = ["sender", "receiver"] } bitcoincore-rpc = "0.14.0" rand = "0.6.5" reqwest = { version = "0.11.4", features = ["blocking", "native-tls-vendored"] } +hyper = "0.14.24" base64 = "0.13.0" clap = "2.33.3" -turn = "0.6.1" tokio = { version = "1.6.1", features = ["full"] } webrtc-util = { version = "0.7.0", default-features = false, features = ["conn", "vnet"] } noiseexplorer_nnpsk0 = { path = "../nnpsk0" } -magic-wormhole = "0.6.0" \ No newline at end of file +url = "2.2.2" \ No newline at end of file diff --git a/payjoin-client/src/http_relay/mod.rs b/payjoin-client/src/http_relay/mod.rs new file mode 100644 index 000000000..30765bedb --- /dev/null +++ b/payjoin-client/src/http_relay/mod.rs @@ -0,0 +1,109 @@ +use rand::{distributions::Alphanumeric, Rng}; +use reqwest::{Body, Client}; +use std::borrow::Cow; + + +pub(crate) struct HttpRelay { + http_relay_url: String, + w_secret: String, +} + +impl HttpRelay { + pub fn new(http_relay_url: String, w_secret: String) -> Self { + println!("http_relay_url: {}", http_relay_url); + Self { + http_relay_url, + w_secret, + } + } + + /// Creates an HTTP Relay `proxy` communication method object. + /// + /// # Arguments + /// + /// * `http_relay_url` - The URL of the HTTP Relay server. + /// * `base_url` - The base URL of the client that will use the proxy communication method. + /// * `server_id` - The ID of the `proxy` communication method server, e.g. https://demo.httprelay.io/proxy/`myServerId`. If not provided, a random string will be generated. + /// * `w_secret` - The write permission secret. If set, it will lock `server_id` and only client requests with this secret can be handled. This ensures that unauthorized handlers are not serving clients. If not provided, a random string will be generated. + /// * `path` - The custom path to the proxy communication method endpoint. Default: `proxy/`. + /// * `asset_path_prefix` - The asset path prefix. + /// + /// # Returns + /// + /// An `HrProxy` object representing the `proxy` communication method. + pub fn proxy(&self) -> HrProxy { + let server_id: Cow = Cow::Owned( + rand::thread_rng() + .sample_iter(&Alphanumeric) + .take(5) + .map(char::from) + .collect() + ); + + let path = "proxy/"; + + let mut owned = self.http_relay_url.clone(); + owned.push_str(path); + println!("owned: {}", owned); + HrProxy::new( + owned, + server_id.as_ref(), + &self.w_secret, + ) + } +} + +pub(crate) struct HrProxy { + server_url: String, + w_secret: String, + job_id: String, + err_retry: usize, +} + +impl HrProxy { + pub fn new(proxy_url: String, server_id: &str, w_secret: &str,) -> Self { + println!("server_url: {}", proxy_url.clone() + server_id); + Self { + server_url: proxy_url + server_id, + w_secret: w_secret.to_string(), + job_id: "".to_string(), + err_retry: 0, + } + } + + pub fn server_url(&self) -> &str { + &self.server_url + } + + /// Start serving client requests + pub async fn serve(&mut self, body: Vec) -> Vec { + let serve = hyper::Method::from_bytes(b"SERVE").unwrap(); + let client = Client::new(); + //loop { + let mut request = client + .request(serve, self.server_url.clone()) + .header("HttpRelay-WSecret", &self.w_secret) + .body(Body::from(body)) + .build() + .expect("Failed to build request"); + + if self.job_id != "" { + request.headers_mut().insert("HttpRelay-Proxy-JobId", self.job_id.parse().unwrap()); + } + println!("request: {:#?}", request); + match client.execute(request).await { + Ok(res) => { + println!("res: {:#?}", res); + if let Some(job_id) = res.headers().get("HttpRelay-Proxy-JobId") { + self.job_id = job_id.to_str().unwrap().to_string(); + } + res.bytes().await.unwrap().to_vec() + }, + Err(e) => { + eprintln!("{}", e.to_string()); + Vec::new() + }, + } + //} + } +} diff --git a/payjoin-client/src/main.rs b/payjoin-client/src/main.rs index 9c00582ec..31b4cbca2 100644 --- a/payjoin-client/src/main.rs +++ b/payjoin-client/src/main.rs @@ -2,27 +2,23 @@ use std::collections::HashMap; use std::convert::TryFrom; use std::convert::TryInto; use std::str::FromStr; -use std::sync::Arc; +use http_relay::HrProxy; use payjoin::bitcoin::hashes::hex::ToHex; -use turn::client::*; -use turn::Error; use bitcoincore_rpc::bitcoin::Address; use bitcoincore_rpc::bitcoin::Amount; use bitcoincore_rpc::RpcApi; use clap::{App, AppSettings, Arg}; use payjoin::bitcoin::util::psbt::PartiallySignedTransaction as Psbt; use payjoin::{PjUriExt, UriExt}; -use tokio::net::UdpSocket; -use webrtc_util::Conn; use noiseexplorer_nnpsk0::noisesession::NoiseSession; use noiseexplorer_nnpsk0::consts::{MAC_LENGTH, DHLEN}; use noiseexplorer_nnpsk0::types::Keypair; -use magic_wormhole::Wormhole; +mod http_relay; #[tokio::main] -async fn main() -> Result<(), Error> { +async fn main() -> Result<(), std::io::Error> { let mut app = App::new("payjoin-client") .version("0.1.0") .author("Dan Gould ") @@ -47,10 +43,10 @@ async fn main() -> Result<(), Error> { .long("bip21") .help("The BIP21 URI to send to") .takes_value(true)) - .arg(Arg::with_name("code") + .arg(Arg::with_name("endpoint") .short("o") - .long("code") - .help("The magic wormhole code to send the payjoin to") + .long("endpoint") + .help("The pj endpoint to send the payjoin to") .takes_value(true)) .arg(Arg::with_name("psk") .short("s") @@ -82,13 +78,13 @@ async fn main() -> Result<(), Error> { if matches.is_present("amount") { let amount = matches.value_of("amount").unwrap(); let amount = Amount::from_sat(amount.parse::().unwrap()); - listen_receiver(amount, bitcoind).await?; + listen_receiver(amount, bitcoind).await; } else { let bip21 = matches.value_of("bip21").unwrap().as_ref(); - let code = matches.value_of("code").unwrap(); let psk = matches.value_of("psk").unwrap(); + let endpoint = matches.value_of("endpoint").unwrap(); let (req, ctx) = create_pj_request(bip21, &bitcoind); //base64 request - let payjoin_psbt = do_send(req, ctx, code, psk).await?; + let payjoin_psbt = do_send(req, ctx, endpoint, psk).await; let psbt = bitcoind.wallet_process_psbt(&serialize_psbt(&payjoin_psbt), None, None, None).unwrap().psbt; let tx = bitcoind.finalize_psbt(&psbt, Some(true)).unwrap().hex.expect("incomplete psbt"); let txid = bitcoind.send_raw_transaction(&tx).unwrap(); @@ -98,26 +94,23 @@ async fn main() -> Result<(), Error> { Ok(()) } -async fn listen_receiver(amount: Amount, bitcoind: bitcoincore_rpc::Client) -> Result<(), Error> { +async fn listen_receiver(amount: Amount, bitcoind: bitcoincore_rpc::Client) { let psk = gen_psk(); - let (welcome, res) = Wormhole::connect_without_code(magic_wormhole::transfer::APP_CONFIG, 2).await.unwrap(); - print!("--code={:?} --psk=\"{}\" ", welcome.code.0, psk); + let relay = "http://localhost:8080/"; + let proxy = crate::http_relay::HttpRelay::new(relay.to_string(), psk[0..8].to_owned()).proxy(); + print!("--endpoint={:?} --psk=\"{}\" ", proxy.server_url(), psk); let pj_receiver_address = bitcoind.get_new_address(None, Some(bitcoincore_rpc::json::AddressType::Bech32)).unwrap(); print_payjoin_uri(pj_receiver_address, amount); // 2. Recv - let mut wormhole = res.await.unwrap(); - process_original_psbt(&mut wormhole, bitcoind, psk).await?; - - wormhole.close().await.unwrap(); - Ok(()) + process_original_psbt(proxy, bitcoind, psk).await; } async fn process_original_psbt( - wormhole: &mut Wormhole, + mut proxy: HrProxy, receiver: bitcoincore_rpc::Client, psk: String, -) -> Result<(), Error> { +) { use payjoin::bitcoin; use payjoin::receiver::UncheckedProposal; use payjoin::bitcoin::consensus; @@ -127,7 +120,8 @@ async fn process_original_psbt( let psk: [u8; 32] = base64::decode(psk).unwrap().try_into().unwrap(); let psk = noiseexplorer_nnpsk0::types::Psk::from_bytes(psk); - let mut buf = wormhole.receive().await.unwrap(); + // TODO pull fallback psbt from relay + let mut buf = proxy.serve(Vec::new()).await; println!("received {} bytes of supposed Original PSBT", buf.len()); // security does not depend on long-term static keys in NNpsk0. The interface still requires a Keypair, but it is not used. @@ -195,16 +189,12 @@ async fn process_original_psbt( in_out.resize(message_b_size, 0); responder.send_message(&mut in_out).unwrap(); // e, ee - wormhole.send(in_out).await.unwrap(); - - Ok(()) + proxy.serve(in_out).await; } -async fn do_send(req: payjoin::sender::Request, ctx: payjoin::sender::Context, code: &str, psk: &str) -> Result { +async fn do_send(req: payjoin::sender::Request, ctx: payjoin::sender::Context, endpoint: &str, psk: &str) -> Psbt { let mut original_psbt = req.body.clone(); //format!("{:?}", tokio::time::Instant::now()); - - // Set up wormhole - let (_, mut wormhole) = Wormhole::connect_with_code(magic_wormhole::transfer::APP_CONFIG, magic_wormhole::Code(code.to_owned())).await.unwrap(); + let w_secret = psk[0..8].to_owned(); // do noise secure handshake let psk: [u8; 32] = base64::decode(psk).unwrap().try_into().unwrap(); @@ -220,10 +210,11 @@ async fn do_send(req: payjoin::sender::Request, ctx: payjoin::sender::Context, c println!("sending in_out: {:?}", in_out); initiator.send_message(&mut in_out).unwrap(); // psk, e println!("sending message_a: {:?}", in_out); - wormhole.send(in_out).await.unwrap(); + let res = reqwest::Client::new().post(endpoint).header("HttpRelay-WSecret", w_secret).body(in_out).send().await.unwrap(); - let mut buf = wormhole.receive().await.unwrap(); + println!("res: {:?}", res); + let mut buf = res.bytes().await.unwrap().to_vec(); // finish noise handshake initiator.recv_message(&mut buf).unwrap(); @@ -232,7 +223,7 @@ async fn do_send(req: payjoin::sender::Request, ctx: payjoin::sender::Context, c let n = payload.len(); let proposal = ctx.process_response(&payload[..n]).unwrap(); println!("proposal: {:#?}", proposal); - Ok(proposal) + proposal } // get a valid BIP 21 URI for payjoin as defined in BIP 78