diff --git a/Cargo.lock b/Cargo.lock index 34839f74d..ac638835f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,35 @@ 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 = "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.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "atty" version = "0.2.14" @@ -28,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" @@ -46,6 +84,12 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + [[package]] name = "base64-compat" version = "1.0.0" @@ -97,7 +141,7 @@ checksum = "0694ea59225b0c5f3cb405ff3f670e4828358ed26aec49dc352f730f0cb1a8a3" dependencies = [ "bech32 0.9.1", "bitcoin_hashes 0.11.0", - "secp256k1 0.24.2", + "secp256k1 0.24.3", "serde", ] @@ -191,11 +235,30 @@ 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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + [[package]] name = "bumpalo" -version = "3.11.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "byteorder" @@ -205,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", @@ -232,9 +295,9 @@ dependencies = [ [[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" @@ -248,12 +311,42 @@ 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 = "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" @@ -297,6 +390,21 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +[[package]] +name = "crc" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" +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 +414,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" @@ -314,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", ] @@ -342,9 +470,9 @@ dependencies = [ [[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", ] @@ -403,47 +531,94 @@ 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.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[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", ] [[package]] name = "futures-core" -version = "0.3.25" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" + +[[package]] +name = "futures-executor" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" +checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] [[package]] name = "futures-io" -version = "0.3.25" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" + +[[package]] +name = "futures-macro" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" +checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[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", "futures-io", + "futures-macro", + "futures-sink", "futures-task", "memchr", "pin-project-lite", @@ -451,6 +626,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" @@ -481,6 +666,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" @@ -505,11 +710,17 @@ 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" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", @@ -547,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", @@ -609,7 +820,7 @@ version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ - "autocfg", + "autocfg 1.1.0", "hashbrown", ] @@ -624,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" @@ -636,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", ] @@ -667,6 +878,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 1.1.0", + "scopeguard", +] + [[package]] name = "log" version = "0.4.17" @@ -678,9 +899,18 @@ dependencies = [ [[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" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" +dependencies = [ + "digest", +] [[package]] name = "memchr" @@ -688,6 +918,15 @@ 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 1.1.0", +] + [[package]] name = "mime" version = "0.3.16" @@ -701,19 +940,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" dependencies = [ "adler", - "autocfg", + "autocfg 1.1.0", ] [[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]] @@ -734,6 +973,36 @@ 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 = "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", + "constant_time_eq", + "hacl-star", + "hex", + "rand 0.6.5", + "zeroize", +] + [[package]] name = "num_cpus" version = "1.15.0" @@ -746,9 +1015,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.16.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "openssl" @@ -782,30 +1051,63 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" 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" version = "0.9.80" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" dependencies = [ - "autocfg", + "autocfg 1.1.0", "cc", "libc", + "openssl-src", "pkg-config", "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.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys 0.45.0", +] + [[package]] name = "payjoin" version = "0.5.1-alpha" dependencies = [ - "base64", + "base64 0.13.1", "bip21", "bitcoin 0.29.2", "bitcoind", "env_logger", "log", - "rand", + "rand 0.8.5", "url", ] @@ -813,10 +1115,33 @@ dependencies = [ name = "payjoin-client" version = "0.1.0" dependencies = [ - "base64", + "base64 0.13.1", + "bitcoincore-rpc 0.14.0", + "clap", + "hyper", + "noiseexplorer_nnpsk0", + "payjoin", + "rand 0.6.5", + "reqwest", + "tokio", + "url", + "webrtc-util", +] + +[[package]] +name = "payjoin-relay" +version = "0.1.0" +dependencies = [ + "base64 0.13.1", "bitcoincore-rpc 0.14.0", + "clap", + "env_logger", + "log", "payjoin", "reqwest", + "tokio", + "turn", + "webrtc-util", ] [[package]] @@ -863,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", ] @@ -898,6 +1223,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" @@ -905,8 +1249,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]] @@ -916,9 +1270,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" @@ -928,6 +1297,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" @@ -939,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", @@ -965,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", + "base64 0.21.0", "bytes", "encoding_rs", "futures-core", @@ -1030,7 +1470,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", @@ -1045,14 +1485,19 @@ checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" [[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]] +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" @@ -1075,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", @@ -1104,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", @@ -1117,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", @@ -1162,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", @@ -1198,15 +1643,30 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +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", + "autocfg 1.1.0", ] +[[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 +1741,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 0.13.1", + "crc", + "lazy_static", + "md-5", + "rand 0.8.5", + "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" @@ -1319,13 +1810,22 @@ 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", ] +[[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" @@ -1406,32 +1906,46 @@ 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", + "autocfg 1.1.0", "bytes", "libc", "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" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", "tokio", @@ -1439,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", @@ -1479,15 +1993,40 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.3" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "turn" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4712ee30d123ec7ae26d1e1b218395a16c87cdbaf4b3925d170d684af62ea5e8" +dependencies = [ + "async-trait", + "base64 0.13.1", + "futures", + "log", + "md-5", + "rand 0.8.5", + "ring", + "stun", + "thiserror", + "tokio", + "webrtc-util", +] + +[[package]] +name = "typenum" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +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" @@ -1504,6 +2043,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" @@ -1516,7 +2061,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", @@ -1546,6 +2091,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" @@ -1576,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", @@ -1586,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", @@ -1601,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", @@ -1613,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", @@ -1623,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", @@ -1636,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", @@ -1669,11 +2220,32 @@ 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 0.8.5", + "thiserror", + "tokio", + "winapi", +] + [[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", @@ -1711,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" @@ -1731,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" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" [[package]] name = "windows_i686_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" - -[[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" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" [[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" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] name = "winreg" @@ -1829,6 +2382,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 e42c3f24d..085099695 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,2 @@ [workspace] -members = ["payjoin", "payjoin-client"] +members = ["payjoin", "payjoin-client", "payjoin-relay", "nnpsk0",] diff --git a/nnpsk0/Cargo.toml b/nnpsk0/Cargo.toml new file mode 100644 index 000000000..b107085fc --- /dev/null +++ b/nnpsk0/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "noiseexplorer_nnpsk0" +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/nnpsk0/src/consts.rs b/nnpsk0/src/consts.rs new file mode 100644 index 000000000..ab231b223 --- /dev/null +++ b/nnpsk0/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/nnpsk0/src/error.rs b/nnpsk0/src/error.rs new file mode 100644 index 000000000..03a570c08 --- /dev/null +++ b/nnpsk0/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/nnpsk0/src/lib.rs b/nnpsk0/src/lib.rs new file mode 100644 index 000000000..e5580a72d --- /dev/null +++ b/nnpsk0/src/lib.rs @@ -0,0 +1,94 @@ +/* +NNpsk0: + -> psk, e + <- 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; + +#[cfg(test)] +mod test { + use crate::consts::{MAC_LENGTH, DHLEN}; + use crate::noisesession::NoiseSession; + use crate::types::{Keypair, Psk}; + + #[test] + 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 psk = Psk::from_bytes(new_256bit_key()); // pre-shared from responder symmetric key. // todo can use 128 bit? + // ... + + // Ready Message A -> psk, e + + // from the initiator (payjoin sender) + 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 + let mut message_a = in_out; + + // from the responder (payjoin receiver) + + 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); + 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" + } + + 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/nnpsk0/src/macros.rs b/nnpsk0/src/macros.rs new file mode 100644 index 000000000..16d7bc9d4 --- /dev/null +++ b/nnpsk0/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/nnpsk0/src/noisesession.rs b/nnpsk0/src/noisesession.rs new file mode 100644 index 000000000..951f388c8 --- /dev/null +++ b/nnpsk0/src/noisesession.rs @@ -0,0 +1,194 @@ +/* ---------------------------------------------------------------- * + * 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. + + /// - `psk`: `Psk` object. Contains the pre-shared key. + pub fn init_session(initiator: bool, prologue: &[u8], s: Keypair, psk: Psk) -> NoiseSession { + if initiator { + NoiseSession{ + hs: HandshakeState::initialize_initiator(prologue, s, 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); + } + 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)?; + self.h = temp.0; + self.is_transport = true; + 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(()) + } + + /// 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); + } + if self.mc == 0 { + self.hs.read_message_a(in_out)?; + } + 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(); + } else if self.i { + self.cs2.read_message_regular(in_out)?; + } else { + self.cs1.read_message_regular(in_out)?; + } + self.mc += 1; + Ok(()) + } +} diff --git a/nnpsk0/src/prims.rs b/nnpsk0/src/prims.rs new file mode 100644 index 000000000..13137943c --- /dev/null +++ b/nnpsk0/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/nnpsk0/src/state.rs b/nnpsk0/src/state.rs new file mode 100644 index 000000000..8cd2b2229 --- /dev/null +++ b/nnpsk0/src/state.rs @@ -0,0 +1,347 @@ +/* ---------------------------------------------------------------- * + * 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, 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); + 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_NNpsk0_25519_ChaChaPoly_BLAKE2s"; + let mut ss: SymmetricState = SymmetricState::initialize_symmetric(&protocol_name[..]); + ss.mix_hash(prologue); + let rs = PublicKey::empty(); + 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.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.decrypt_and_hash(in_out)?; + 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/nnpsk0/src/types.rs b/nnpsk0/src/types.rs new file mode 100644 index 000000000..17aa0ba84 --- /dev/null +++ b/nnpsk0/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_nnpsk0::{ + /// # 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_nnpsk0::{ + /// # 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_nnpsk0::{ + /// # 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_nnpsk0::{ + /// # 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_nnpsk0::{ + /// # 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_nnpsk0::{ + /// # 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_nnpsk0::{ + /// # 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_nnpsk0::{ + /// # 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/nnpsk0/src/utils.rs b/nnpsk0/src/utils.rs new file mode 100644 index 000000000..e5c684501 --- /dev/null +++ b/nnpsk0/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.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 19648cd0f..0b8f6816b 100644 --- a/payjoin-client/Cargo.toml +++ b/payjoin-client/Cargo.toml @@ -7,7 +7,14 @@ 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"] } +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" +tokio = { version = "1.6.1", features = ["full"] } +webrtc-util = { version = "0.7.0", default-features = false, features = ["conn", "vnet"] } +noiseexplorer_nnpsk0 = { path = "../nnpsk0" } +url = "2.2.2" \ 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/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 4ff20cd2e..31b4cbca2 100644 --- a/payjoin-client/src/main.rs +++ b/payjoin-client/src/main.rs @@ -1,46 +1,264 @@ use std::collections::HashMap; use std::convert::TryFrom; +use std::convert::TryInto; +use std::str::FromStr; +use http_relay::HrProxy; +use payjoin::bitcoin::hashes::hex::ToHex; +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 noiseexplorer_nnpsk0::noisesession::NoiseSession; +use noiseexplorer_nnpsk0::consts::{MAC_LENGTH, DHLEN}; +use noiseexplorer_nnpsk0::types::Keypair; -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"); +mod http_relay; - let cookie_file = args.next().expect("Missing arguments: cookie_file bip21"); +#[tokio::main] +async fn main() -> Result<(), std::io::Error> { + 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)) + .arg(Arg::with_name("endpoint") + .short("o") + .long("endpoint") + .help("The pj endpoint 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("amount") + .short("a") + .long("amount") + .help("The amount to request in satoshis") + .takes_value(true)); - let bip21 = - args.next().expect("Missing arguments: bip21").into_string().expect("bip21 is not UTF-8"); + let matches = app.clone().get_matches(); - let link = payjoin::Uri::try_from(&*bip21).unwrap(); + if matches.is_present("FULLHELP") { + app.print_long_help().unwrap(); + return Ok(()); + } - let link = link - .check_pj_supported() - .unwrap_or_else(|_| panic!("The provided URI doesn't support payjoin (BIP78)")); + let port = matches.value_of("port").unwrap(); + let cookie_file = matches.value_of("cookie-file").unwrap(); + + let bitcoind = bitcoincore_rpc::Client::new( + &format!("http://127.0.0.1:{}", port), + bitcoincore_rpc::Auth::CookieFile(cookie_file.into()), + ) + .unwrap(); + + 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; + } else { + let bip21 = matches.value_of("bip21").unwrap().as_ref(); + 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, 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(); + println!("Sent tx: {}", txid); + } + + Ok(()) +} + +async fn listen_receiver(amount: Amount, bitcoind: bitcoincore_rpc::Client) { + let psk = gen_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 + process_original_psbt(proxy, bitcoind, psk).await; +} + +async fn process_original_psbt( + mut proxy: HrProxy, + receiver: bitcoincore_rpc::Client, + psk: String, +) { + use payjoin::bitcoin; + use payjoin::receiver::UncheckedProposal; + use payjoin::bitcoin::consensus; + 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); + + // 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. + let mut responder = NoiseSession::init_session(false, b"", Keypair::new_empty(), psk); + 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 + // 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(&payload[..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(); + 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, 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, + 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 payjoin_proposal_psbt = base64::encode(consensus::serialize(&payjoin_proposal_psbt)); + + // 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 + + proxy.serve(in_out).await; +} + +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()); + let w_secret = psk[0..8].to_owned(); + + // 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); + + let res = reqwest::Client::new().post(endpoint).header("HttpRelay-WSecret", w_secret).body(in_out).send().await.unwrap(); + + println!("res: {:?}", res); + let mut buf = res.bytes().await.unwrap().to_vec(); + + // finish noise handshake + 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: {:#?}", proposal); + proposal +} + +// 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(), + mock_endpoint, + ); + 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 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 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)), @@ -59,24 +277,8 @@ fn main() { 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 (req, ctx) = link.create_pj_request(psbt, pj_params).unwrap(); - 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(); - 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(); + let pj_params = payjoin::sender::Configuration::non_incentivizing(); + link.create_pj_request(psbt, pj_params).unwrap() } fn load_psbt_from_base64( @@ -102,3 +304,31 @@ 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, + } + } +} + +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 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 new file mode 100644 index 000000000..c6a076eca --- /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", "native-tls-vendored"] } +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" diff --git a/payjoin-relay/src/main.rs b/payjoin-relay/src/main.rs new file mode 100644 index 000000000..76557c297 --- /dev/null +++ b/payjoin-relay/src/main.rs @@ -0,0 +1,140 @@ +use turn::auth::*; +use turn::relay::relay_range::RelayAddressGeneratorRanges; +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("pj.chaincase.app") + .takes_value(true) + .long("realm") + .help("Realm (defaults to \"pj.chaincase.app\")"), + ) + .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(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)), + }), + }], + 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(()) +}