From 4af69eb3ddfbfd8e8dd145578f44a6ec480673ac Mon Sep 17 00:00:00 2001 From: Chris Tam Date: Tue, 18 Feb 2025 16:21:47 +0800 Subject: [PATCH] init --- .env.sample | 2 + .gitignore | 3 + Cargo.lock | 1764 ++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 17 + README.md | 419 ++++++++++++ Screenshot.png | Bin 0 -> 22462 bytes src/main.rs | 394 +++++++++++ 7 files changed, 2599 insertions(+) create mode 100644 .env.sample create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 Screenshot.png create mode 100644 src/main.rs diff --git a/.env.sample b/.env.sample new file mode 100644 index 0000000..07e7d15 --- /dev/null +++ b/.env.sample @@ -0,0 +1,2 @@ +PORT=8001 +USER_ID=1 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f570b1e --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +database*.json +.env \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..d71aa4e --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1764 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "actix-codec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617a8268e3537fe1d8c9ead925fca49ef6400927ee7bc26750e90ecee14ce4b8" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-sink", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "actix-cors" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0346d8c1f762b41b458ed3145eea914966bb9ad20b9be0d6d463b20d45586370" +dependencies = [ + "actix-utils", + "actix-web", + "derive_more", + "futures-util", + "log", + "once_cell", + "smallvec", +] + +[[package]] +name = "actix-http" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2079246596c18b4a33e274ae10c0e50613f4d32a4198e09c7b93771013fed74" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "ahash 0.8.3", + "base64", + "bitflags", + "brotli", + "bytes", + "bytestring", + "derive_more", + "encoding_rs", + "flate2", + "futures-core", + "h2", + "http", + "httparse", + "httpdate", + "itoa", + "language-tags", + "local-channel", + "mime", + "percent-encoding", + "pin-project-lite", + "rand", + "sha1", + "smallvec", + "tokio", + "tokio-util", + "tracing", + "zstd", +] + +[[package]] +name = "actix-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "actix-router" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66ff4d247d2b160861fa2866457e85706833527840e4133f8f49aa423a38799" +dependencies = [ + "bytestring", + "http", + "regex", + "serde", + "tracing", +] + +[[package]] +name = "actix-rt" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15265b6b8e2347670eb363c47fc8c75208b4a4994b27192f345fcbe707804f3e" +dependencies = [ + "futures-core", + "tokio", +] + +[[package]] +name = "actix-server" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e8613a75dd50cc45f473cee3c34d59ed677c0f7b44480ce3b8247d7dc519327" +dependencies = [ + "actix-rt", + "actix-service", + "actix-utils", + "futures-core", + "futures-util", + "mio", + "num_cpus", + "socket2", + "tokio", + "tracing", +] + +[[package]] +name = "actix-service" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a" +dependencies = [ + "futures-core", + "paste", + "pin-project-lite", +] + +[[package]] +name = "actix-utils" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" +dependencies = [ + "local-waker", + "pin-project-lite", +] + +[[package]] +name = "actix-web" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd3cb42f9566ab176e1ef0b8b3a896529062b4efc6be0123046095914c4c1c96" +dependencies = [ + "actix-codec", + "actix-http", + "actix-macros", + "actix-router", + "actix-rt", + "actix-server", + "actix-service", + "actix-utils", + "actix-web-codegen", + "ahash 0.7.6", + "bytes", + "bytestring", + "cfg-if", + "cookie", + "derive_more", + "encoding_rs", + "futures-core", + "futures-util", + "http", + "itoa", + "language-tags", + "log", + "mime", + "once_cell", + "pin-project-lite", + "regex", + "serde", + "serde_json", + "serde_urlencoded", + "smallvec", + "socket2", + "time", + "url", +] + +[[package]] +name = "actix-web-codegen" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2262160a7ae29e3415554a3f1fc04c764b1540c116aa524683208078b7a75bc9" +dependencies = [ + "actix-router", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "async-trait" +version = "0.1.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "brotli" +version = "3.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b6561fd3f895a11e8f72af2cb7d22e08366bebc2b6b57f7744c4bda27034744" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "bytestring" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "238e4886760d98c4f899360c834fa93e62cf7f721ac3c2da375cbdf4b8679aae" +dependencies = [ + "bytes", +] + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +dependencies = [ + "jobserver", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cookie" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "cpufeatures" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +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 = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + +[[package]] +name = "dske_poc_client" +version = "0.1.0" +dependencies = [ + "actix-cors", + "actix-web", + "async-trait", + "dotenv", + "rand", + "reqwest", + "serde", + "serde_json", + "tokio", +] + +[[package]] +name = "encoding_rs" +version = "0.8.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "flate2" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "h2" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "ipnet" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "jobserver" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "language-tags" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.146" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "local-channel" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f303ec0e94c6c54447f84f3b0ef7af769858a9c4ef56ef2a986d3dcd4c3fc9c" +dependencies = [ + "futures-core", + "futures-sink", + "futures-util", + "local-waker", +] + +[[package]] +name = "local-waker" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e34f76eb3611940e0e7d53a9aaa4e6a3151f69541a282fd0dad5571420c53ff1" + +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "openssl" +version = "0.10.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69b3f656a17a6cbc115b5c7a40c616947d213ba182135b014d6051b73ab6f019" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2ce0f250f34a308dcfdbb351f511359857d4ed2134ba715a4eadd46e1ffd617" +dependencies = [ + "cc", + "libc", + "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.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "paste" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" + +[[package]] +name = "reqwest" +version = "0.11.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.37.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "schannel" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +dependencies = [ + "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 = "security-framework" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + +[[package]] +name = "serde" +version = "1.0.163" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.163" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "serde_json" +version = "1.0.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[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.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" +dependencies = [ + "autocfg", + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "time" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc" +dependencies = [ + "itoa", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" + +[[package]] +name = "time-macros" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" +dependencies = [ + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.28.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" +dependencies = [ + "autocfg", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "log", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.18", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d1985d03709c53167ce907ff394f5316aa22cb4e12761295c5dc57dacb6297e" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" + +[[package]] +name = "web-sys" +version = "0.3.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + +[[package]] +name = "zstd" +version = "0.12.3+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76eea132fb024e0e13fd9c2f5d5d595d8a967aa72382ac2f9d39fcc95afd0806" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "6.0.5+zstd.1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56d9e60b4b1758206c238a10165fbcae3ca37b01744e394c463463f6529d23b" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.8+zstd.1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" +dependencies = [ + "cc", + "libc", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..d3179bc --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "dske_poc_client" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +actix-web = "4.3.1" +dotenv = "0.15.0" +reqwest = { version = "0.11.17", features = ["json"] } +serde = { version = "1.0.160", features = ["derive"] } +serde_json = "1.0.96" +tokio = { version = "1.28.0", features = ["full"] } +async-trait = "0.1.68" +actix-cors = "0.6.4" +rand = "0.8.5" diff --git a/README.md b/README.md new file mode 100644 index 0000000..5c6ad82 --- /dev/null +++ b/README.md @@ -0,0 +1,419 @@ +# DSKE POC (Client) + +## Introduction + +This is a proof of concept for the DSKE system. The main goal is to demonstrate the algorithm in DSKE. There are a few assumptions and limitations in this proof-of-concept project: + +1. This is not a working protocol, this project is just to prove the algorithm concept. +2. This project is demonstrating a simple DSKE protocol, which means n = k. It is a (n, n) secret sharing scheme. +3. The code assumes that the final key sender chooses all common security hubs with the receiver that they both register. + +This project is separated into two parts: the client and the security hub server. + +- [client](https://gitea.devchristam.com/devchristam/dske_poc_client) +- [server](https://gitea.devchristam.com/devchristam/dske_poc_server) + +You need to host multiple clients and servers to make this proof-of-concept DSKE system work. + +## DSKE Main Phases + +1. PSKM Generation and Distribution + + The security hub's `POST /register_client` endpoint will generate random bits and record the client on the server side. This endpoint will return the generated random bits. The client's `POST /register_securityhub` endpoint will store these random bits so that both the security hub and the client have a copy. Note that the distribution process needs to be done manually because real PSKM distribution is done physically. + +2. Peer Identity Establishment + + When a client wants to share a key with another client using `POST /make_connection`, the sender client will send a request to the receiver client's `POST /agree_connection` endpoint with a list of security hubs to use in the key exchange. The receiver client will check if both the sender and receiver are registered in those security hubs using the security hub's `POST /user_list` endpoint. This endpoint will display every client ID registered in the security hub, allowing the client to identify common security hubs. + +3. Key Agreement + + The sender will use the `POST /make_connection` endpoint to exchange keys with the receiver. + + 1. Share Generation + + The proof-of-concept project will use an (n, n) secret sharing scheme in a simple DSKE protocol. For simplicity, the sender will choose all the common security hubs with the receiver. + + 2. Share Distribution + + The sender will send a request to the security hub's `POST /generate_key_instruction` endpoint to generate a key instruction A (sender's random bits) XOR B (receiver's random bits). + + 3. Key Reconstruction + + After the security hub sends a request to the client's `POST /receive_key_instruction` endpoint, the client will calculate A ^ B ^ B to retrieve A in each request and store the calculated A in a key-value pair using the final key hash as the key. The receiver can reconstruct the final key S by executing XOR on every share in the simple DSKE protocol (n, n) secret sharing scheme. + +4. Key Validation + + The receiver will only reconstruct the final key if the number of received shares equals the total number of shares. The exchange process will abort if the numbers do not match. + +## Step-by-Step Setup Guide + +This guide will walk you through the steps to set up and run the DSKE POC server and client. + +### Steps + +#### 1. Clone the Repository + +Clone the repository to your local machine using the following commands: + +```bash +# Clone the DSKE POC server +git clone https://gitea.devchristam.com/devchristam/dske_poc_server.git + +# Clone the DSKE POC client +git clone https://gitea.devchristam.com/devchristam/dske_poc_client.git +``` + +#### 2. Build the Project + +Navigate to the project directory and build the project using Cargo: + +```sh +cargo build +``` + +#### 3. Run the Server and Client + +Run the server and client using the following commands. You need to use Tmux or open multiple terminal applications to host multiple servers and clients on the same machine. + +For server: + +```sh +# The code supports .env for port and security hub ID settings. +# Even if not using the .env file, please copy the .env because missing variables will show a runtime error. +cp .env.sample .env +# set the environment variables in .env if needed +# cargo run -- +# e.g. +cargo run -- 8081 1 +cargo run -- 8082 2 +cargo run -- 8083 3 +``` + +For client: + +```sh +# The code supports .env for port and security hub ID settings. +# Even if not using the .env file, please copy the .env because missing variables will show a runtime error. +cp .env.sample .env +# set the environment variables in .env if needed +# cargo run -- +# e.g. +cargo run -- 8001 1 +cargo run -- 8002 2 +``` + +## 4. Exchange Key + +### Steps + +All API calls use `content-type: "application/json"`. + +#### 1. Register Client in Security Hub + +Use the security hub endpoint `POST /register_client` to record the client. + +- Example (record a client running on `cargo run -- 8001 1`): +```json +{ + "id": 1, + "username": "alice", + "url": "http://localhost:8001" +} +``` + +- This request will return a payload. You need to copy the `random_bits` array for step 2: + +```json +{ + "id": 1, + "username": "alice", + "url": "http://localhost:8001", + "random_bits": [ + false, + false, + true, + true, + true, + ... + ] +} +``` + +#### 2. Register Security Hub in the Client + +Copy the `random_bits` array and input the same content into the client endpoint `POST /register_securityhub`. + +- Example (record a security hub running on `cargo run -- 8081 1`): + +```json +{ + "securityhub_id": 1, + "securityhub_url": "http://localhost:8081", + "random_bits": [ + false, + false, + true, + true, + true, + ... + ] +} +``` + +After repeating steps 1 and 2, you can create a proof-of-concept DSKE system network. Then the client can exchange keys within the network. + +#### 3. Exchange Key + +In the client endpoint `POST /make_connection`, you can exchange the final key with another client. + +- Example (from client `cargo run -- 8001 1` exchanging key with `cargo run -- 8002 2`): +```json +{ + "client_id": 2, + "client_url": "http://localhost:8002", + "keysize": 5 +} +``` + +![screenshot](./Screenshot.png) + +## API Documentation + +### Server + +#### `POST /register_client` + +Registers a client in the security hub. + +- **Input:** + + `content-type: "application/json"` + + ```json + { + "id": Integer, + "username": String, + "url": String + } + ``` + + Example: + + ```json + { + "id": 999, + "username": "test", + "url": "http://localhost:8888" + } + ``` + +- **Output:** + + ```json + { + "id": Integer, + "username": String, + "url": String, + "random_bits": Boolean[] + } + ``` + + Example: + + ```json + { + "id": 999, + "username": "test", + "url": "http://localhost:8888", + "random_bits": [true, false, true, ...] + } + ``` + +#### `POST /user_list` + +Retrieves the list of registered user IDs from the security hub. + +- **Input:** + + No input required. + +- **Output:** + + ```json + Integer[] + ``` + + Example: + + ```json + [1, 2, 3, ...] + ``` + +#### `POST /generate_key_instruction` + +Generates a key instruction for the key exchange process. + +- **Input:** + + `content-type: "application/json"` + + ```json + { + "sender_id": Integer, + "receiver_id": Integer, + "hash": Integer, + "keysize": Integer + } + ``` + + Example: + + ```json + { + "sender_id": 1, + "receiver_id": 2, + "hash": 12345, + "keysize": 5 + } + ``` + +- **Output:** + + ```json + Boolean[] + ``` + + Example: + + ```json + [true, false, true, ...] + ``` + +### Client + +#### `POST /register_securityhub` + +Registers a security hub in the client. + +- **Input:** + + `content-type: "application/json"` + + ```json + { + "securityhub_id": Integer, + "securityhub_url": String, + "random_bits": Boolean[] + } + ``` + + Example: + + ```json + { + "securityhub_id": 1, + "securityhub_url": "http://localhost:8081", + "random_bits": [true, false, true, ...] + } + ``` + +- **Output:** + + Null + +#### `POST /make_connection` + +Initiates a key exchange with another client. + +- **Input:** + + `content-type: "application/json"` + + ```json + { + "client_id": Integer, + "client_url": String, + "keysize": Integer + } + ``` + + Example: + + ```json + { + "client_id": 2, + "client_url": "http://localhost:8002", + "keysize": 5 + } + ``` + +- **Output:** + + Null + +#### `POST /agree_connection` + +Agrees to a key exchange initiated by another client. Check the `securityhub_list` are both registered or not. + +- **Input:** + + `content-type: "application/json"` + + ```json + { + "client_id": Integer, + "securityhub_list": Integer[], + "keysize": Integer + } + ``` + + Example: + + ```json + { + "client_id": 1, + "securityhub_list": [1, 2], + "keysize": 5 + } + ``` + +- **Output:** + + Null + +If the exchange cannot process, the API will not return `200 OK`. + + +#### `POST /receive_key_instruction` + +Receives a key instruction from the security hub. + +- **Input:** + + `content-type: "application/json"` + + ```json + { + "sender_id": Integer, + "hash": Integer, + "key_instruction": Boolean[], + "total": Integer, + "securityhub_id": Integer + } + ``` + + Example: + + ```json + { + "sender_id": 1, + "hash": 1234, + "key_instruction": [true, false, ...], + "total": 2, + "securityhub_id": 1 + } + ``` + +- **Output:** + + Null \ No newline at end of file diff --git a/Screenshot.png b/Screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..17d9e3db28d924b64864bd4a1c59b5720573da31 GIT binary patch literal 22462 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 MzMm|L_PSpFCu>rxC;$Ke literal 0 HcmV?d00001 diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..a60d735 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,394 @@ +use actix_cors::Cors; +use actix_web::{ http::header, web, App, HttpServer, Responder, HttpResponse }; +use serde::{ Deserialize, Serialize }; +use reqwest::Client as HttpClient; +use std::collections::hash_map::DefaultHasher; +use std::sync::Mutex; +use std::collections::HashMap; +use std::fs; +use std::env; +use std::io::Write; +use dotenv::dotenv; +use std::hash::{Hash, Hasher}; + +#[derive(Serialize, Deserialize, Debug, Clone)] +struct PSKM { + securityhub_id: u64, + securityhub_url: String, + random_bits: Vec +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +struct MakeConnection { + client_id: u64, + client_url: String, + keysize: u64 +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +struct KeyInstruction{ + sender_id: u64, + securityhub_id: u64, + hash: u64, + total: u64, + key_instruction: Vec +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +struct AgreeConnection { + client_id: u64, + securityhub_list: Vec, + keysize: u64 +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +struct ReceiveInstruction { + sender_id: u64, + securityhub_id: u64, + calculated: Vec, + total: u64 +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +struct Database { + received: HashMap>, + pskm_list: HashMap, +} + +impl Database { + fn new() -> Self { + Self { + received: HashMap::new(), + pskm_list: HashMap::new() + } + } + + fn get_pskm(&self, id: &u64) -> Option<&PSKM> { + self.pskm_list.get(id) + } + + fn insert_pskm(&mut self, pskm: PSKM) { + self.pskm_list.insert(pskm.securityhub_id, pskm); + } + + fn insert_received(&mut self, hash: u64,received: ReceiveInstruction) { + let received_list = self.received.entry(hash).or_insert(Vec::new()); + received_list.push(received); + } + + fn get_received(&self, hash: &u64) -> Option<&Vec> { + self.received.get(hash) + } + + // remove the first n elements from the random_bits array + fn remove_random_bits(&mut self, id: &u64, n: usize) { + let pskm = self.pskm_list.get_mut(id).unwrap(); + pskm.random_bits = pskm.random_bits.split_off(n); + } + + fn save_to_file(&self) -> std::io::Result<()> { + let data: String = serde_json::to_string(&self)?; + let args: Vec = env::args().collect(); + let file_name = format!("database-{}.json", args.get(2).unwrap_or(&env::var("USER_ID").unwrap())); + let mut file: fs::File = fs::File::create(file_name)?; + file.write_all(data.as_bytes())?; + Ok(()) + } + + fn load_from_file() -> std::io::Result { + let args: Vec = env::args().collect(); + let file_name = format!("database-{}.json", args.get(2).unwrap_or(&env::var("USER_ID").unwrap())); + let file_content: String = fs::read_to_string(file_name)?; + let db: Database = serde_json::from_str(&file_content)?; + Ok(db) + } +} + +struct AppState { + db: Mutex +} + +async fn register_securityhub(app_state: web::Data, pskm: web::Json) -> impl Responder { + let mut db: std::sync::MutexGuard = app_state.db.lock().unwrap(); + db.insert_pskm(pskm.into_inner()); + let _ = db.save_to_file(); + HttpResponse::Ok().finish() +} + +async fn make_connection(app_state: web::Data, make_connection: web::Json) -> impl Responder { + let mut db: std::sync::MutexGuard = app_state.db.lock().unwrap(); + let common_securityhubs = check_common_securityhub(make_connection.client_id).await; + + //loop through the common_securityhubs and check if the keysize is bigger than the random_bits array size + for id in common_securityhubs.iter() { + let pskm = db.get_pskm(id).unwrap(); + if pskm.random_bits.len() < make_connection.keysize as usize { + return HttpResponse::BadRequest().body("Keysize bigger than random bits array size"); + } + } + + println!("Common securityhubs: {:?}", common_securityhubs); + + // if there is no common securityhub, abort the key exchange process + if common_securityhubs.len() == 0 { + return HttpResponse::BadRequest().body("No common securityhub found"); + } + + // send request to another client `/agree_connection` with the common_securityhubs list + let client = HttpClient::new(); + let res = client.post(&format!("{}/agree_connection", make_connection.client_url)) + .json(&AgreeConnection { + client_id: make_connection.client_id, + securityhub_list: common_securityhubs.clone(), + keysize: make_connection.keysize, + }) + .send() + .await; + + match res { + Ok(response) => { + if response.status().is_client_error() { + return HttpResponse::BadRequest().body("Receiver disagree connection"); + } + }, + Err(err) => { + println!("Error connecting to receiver: {:?}", err); + return HttpResponse::BadRequest().body("Error connecting to receiver"); + } + }; + + // print the final key by the common_securityhubs + let final_key = generate_final_key(make_connection.keysize, common_securityhubs.clone()); + let final_key_string = get_final_key_string(final_key.clone()); + + println!{"Final key: {}", final_key_string}; + + // hash the final key + let mut hasher = DefaultHasher::new(); + + final_key.hash(&mut hasher); + let hashed_value = hasher.finish(); + + println!("Hashed final key: {:?}", hashed_value); + + //send a request to all common_securityhubs `generate_key_instruction` + let args: Vec = env::args().collect(); + for id in common_securityhubs.iter() { + let pskm = db.get_pskm(id).unwrap(); + let client = HttpClient::new(); + let user_id_env = env::var("USER_ID").unwrap(); + let user_id: u64 = args.get(2).unwrap_or(&user_id_env).parse().unwrap(); + + let res = client.post(&format!("{}/generate_key_instruction", pskm.securityhub_url)) + .json(&serde_json::json!({ + "sender_id": user_id, + "receiver_id": make_connection.client_id, + "hash": hashed_value, + "total": common_securityhubs.len(), + "keysize": make_connection.keysize + })) + .send() + .await; + + match res { + Ok(response) => { + println!("Request sent to securityhub {}", id); + // remove random bits after sending the request + db.remove_random_bits(id, make_connection.keysize as usize); + let _ = db.save_to_file(); + if response.status().is_client_error() { + println!("securityhub {} error", id); + } + }, + Err(err) => { + println!("Error sending request to securityhub {}: {:?}", id, err); + } + } + } + + HttpResponse::Ok().finish() +} + +fn get_final_key_string(final_key: Vec) -> std::string::String { + let mut final_key_string: String = String::new(); + for bit in final_key { + final_key_string.push_str(if bit { "1" } else { "0" }); + } + final_key_string +} + +fn generate_final_key(key_size: u64, securityhub_list: Vec) -> Vec { + let db: Database = Database::load_from_file().unwrap(); + let mut final_key: Vec = Vec::new(); + for i in 0..key_size { + let xor_result = db.pskm_list.iter() + .filter(|(_, pskm)| securityhub_list.contains(&pskm.securityhub_id)) + .map(|(_, pskm)| pskm.random_bits[i as usize]) + .fold(false, |acc, bit| acc ^ bit); + final_key.push(xor_result); + } + + final_key +} + +async fn check_common_securityhub(client_id: u64) -> Vec { + let db: Database = Database::load_from_file().unwrap(); + // get every registered securityhub + let pskm_list = db.pskm_list.clone(); + + // for every securityhub, send a request to the securityhub `/user_list` + // store the securityhub id if the response contains the target client id + let mut found_in: Vec = Vec::new(); + for (id, pskm) in pskm_list { + let client = HttpClient::new(); + let res = client.post(&format!("{}/user_list", pskm.securityhub_url)) + .send() + .await; + + if let Err(err) = res { + println!("Error connecting to securityhub {}: {:?}", id, err); + continue; + } + + let res = res.unwrap(); + + let payload = res.json::().await; + + if let Err(err) = payload { + println!("Error parsing JSON: {:?}", err); + continue; + } + + let payload = payload.unwrap(); + + // check if the client id is in the array + for user in payload.as_array().unwrap() { + if user == &client_id { + found_in.push(id); + } + } + } + + found_in +} + +// process to exchnage key if the input securityhub in securityhub_list are all found in the common securityhub +async fn agree_connection(app_state: web::Data, agree_connection: web::Json) -> impl Responder { + let db: std::sync::MutexGuard = app_state.db.lock().unwrap(); + let common_securityhubs = check_common_securityhub(agree_connection.client_id).await; + // get every registered securityhub + let pskm_list = db.pskm_list.clone(); + + // check agree_connection.securityhub_list, if the id is not in the common_list, or the keysize is bigger than the random_bits array size, return an error + for id in agree_connection.securityhub_list.iter() { + if !common_securityhubs.contains(id) { + return HttpResponse::BadRequest().body("Securityhub not in common list"); + } + + // check remaining random bits + let pskm = pskm_list.get(id).unwrap(); + if pskm.random_bits.len() < agree_connection.keysize as usize { + return HttpResponse::BadRequest().body("Keysize bigger than random bits array size"); + } + } + + HttpResponse::Ok().finish() +} + +async fn receive_key_instruction(app_state: web::Data, key_instruction: web::Json) -> impl Responder { + let mut db: std::sync::MutexGuard = app_state.db.lock().unwrap(); + // println!("Key instruction received: {:?}", key_instruction); + + // calculate A ^ B ^ B + let mut calculated = Vec::new(); + let securiyhub_random_bits = db.get_pskm(&key_instruction.securityhub_id).unwrap().random_bits.clone(); + for (index, bit) in key_instruction.key_instruction.iter().enumerate() { + calculated.push(bit ^ securiyhub_random_bits.get(index).unwrap()); + } + // println!("Calculated random bit A: {:?}", calculated); + + // store the calculated key in the db received + db.insert_received(key_instruction.hash, ReceiveInstruction { + sender_id: key_instruction.sender_id, + securityhub_id: key_instruction.securityhub_id, + calculated, + total: key_instruction.total + }); + + // remove the used random bits + db.remove_random_bits(&key_instruction.securityhub_id, key_instruction.key_instruction.len()); + + // if the received list is equal to the total, calculate the final key + let received_list = db.get_received(&key_instruction.hash).unwrap(); + if received_list.len() == key_instruction.total as usize { + let final_key: Vec = (0..key_instruction.key_instruction.len()) + .map(|i| { + received_list.iter() + .map(|received| received.calculated[i]) + .fold(false, |acc, bit| acc ^ bit) + }) + .collect(); + + // remove the received list from the db + db.received.remove(&key_instruction.hash); + + let final_key_string = get_final_key_string(final_key.clone()); + println!{"Received final key: {}", final_key_string}; + } + else if received_list.len() > key_instruction.total as usize { + return HttpResponse::BadRequest().body("Received list is bigger than the total"); + } + + let _ = db.save_to_file(); + HttpResponse::Ok().finish() +} + + +#[actix_web::main] +async fn main() -> std::io::Result<()> { + dotenv().ok(); + let args: Vec = env::args().collect(); + let db: Database = match Database::load_from_file() { + Ok(db) => db, + Err(_) => Database::new() + }; + + let data: web::Data = web::Data::new(AppState { + db: Mutex::new(db) + }); + + // if args[1] is set, use the port from the args + // else use the port from the .env + let port_env = env::var("PORT").unwrap(); + let port = args.get(1).unwrap_or(&port_env); + println!("Starting client on port {}", port); + + // if args[2] is set, use the user id from the args + // else use the user id from the .env + let client_id_env = env::var("USER_ID").unwrap(); + let client_id = args.get(2).unwrap_or(&client_id_env); + println!("Client ID: {}", client_id); + + HttpServer::new(move || { + App::new() + .wrap( + Cors::permissive() + .allowed_origin_fn(|origin, _req_head| { + origin.as_bytes().starts_with(b"http://localhost") || origin == "null" + }) + .allowed_methods(vec!["GET", "POST", "PUT", "DELETE"]) + .allowed_headers(vec![header::AUTHORIZATION, header::ACCEPT]) + .allowed_header(header::CONTENT_TYPE) + .supports_credentials() + .max_age(3600) + ) + .app_data(data.clone()) + .route("/register_securityhub", web::post().to(register_securityhub)) + .route("/make_connection", web::post().to(make_connection)) + .route("/agree_connection", web::post().to(agree_connection)) + .route("/receive_key_instruction", web::post().to(receive_key_instruction)) + }) + .bind(format!("127.0.0.1:{}", port))? + .run() + .await +}