diff --git a/Cargo.lock b/Cargo.lock index f98ce02..780c76c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,30 +3,38 @@ version = 3 [[package]] -name = "addr2line" -version = "0.24.2" +name = "aead" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ - "gimli", + "crypto-common", + "generic-array", ] [[package]] -name = "adler2" -version = "2.0.0" +name = "aes" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" - -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", - "once_cell", - "version_check", - "zerocopy", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", ] [[package]] @@ -36,92 +44,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] -name = "async-trait" -version = "0.1.83" +name = "block-buffer" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "proc-macro2", - "quote", - "syn", + "generic-array", ] -[[package]] -name = "axum" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" -dependencies = [ - "async-trait", - "axum-core", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-util", - "itoa", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "serde_json", - "serde_path_to_error", - "serde_urlencoded", - "sync_wrapper 1.0.1", - "tokio", - "tower", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axum-core" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "mime", - "pin-project-lite", - "rustversion", - "sync_wrapper 1.0.1", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "backtrace" -version = "0.3.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-targets", -] - -[[package]] -name = "bitflags" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" - [[package]] name = "bumpalo" version = "3.16.0" @@ -131,100 +61,162 @@ dependencies = [ "allocator-api2", ] -[[package]] -name = "bytes" -version = "1.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" - [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "rand_core", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rustc_version", + "subtle", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "depell" version = "0.1.0" dependencies = [ - "axum", - "rusqlite", + "aes-gcm", + "ed25519-dalek", + "getrandom", + "rand_core", + "x25519-dalek", ] [[package]] -name = "fallible-iterator" -version = "0.3.0" +name = "digest" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" - -[[package]] -name = "fallible-streaming-iterator" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "form_urlencoded" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "percent-encoding", + "block-buffer", + "crypto-common", ] [[package]] -name = "futures-channel" -version = "0.3.30" +name = "ed25519" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ - "futures-core", + "signature", ] [[package]] -name = "futures-core" -version = "0.3.30" +name = "ed25519-dalek" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" - -[[package]] -name = "futures-task" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" - -[[package]] -name = "futures-util" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ - "futures-core", - "futures-task", - "pin-project-lite", - "pin-utils", + "curve25519-dalek", + "ed25519", + "rand_core", + "sha2", + "subtle", ] [[package]] -name = "gimli" -version = "0.31.1" +name = "fiat-crypto" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[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.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", -] [[package]] name = "hashbrown" @@ -232,15 +224,6 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" -[[package]] -name = "hashlink" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" -dependencies = [ - "hashbrown 0.14.5", -] - [[package]] name = "hbbytecode" version = "0.1.0" @@ -276,136 +259,26 @@ dependencies = [ ] [[package]] -name = "hermit-abi" -version = "0.3.9" +name = "inout" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - -[[package]] -name = "html-macro" -version = "0.1.0" - -[[package]] -name = "http" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ - "bytes", - "fnv", - "itoa", + "generic-array", ] -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" -dependencies = [ - "bytes", - "futures-util", - "http", - "http-body", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "hyper" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "smallvec", - "tokio", -] - -[[package]] -name = "hyper-util" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" -dependencies = [ - "bytes", - "futures-util", - "http", - "http-body", - "hyper", - "pin-project-lite", - "tokio", - "tower-service", -] - -[[package]] -name = "itoa" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" - [[package]] name = "libc" version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" -[[package]] -name = "libsqlite3-sys" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" -dependencies = [ - "pkg-config", - "vcpkg", -] - [[package]] name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" -[[package]] -name = "matchit" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" - -[[package]] -name = "memchr" -version = "2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" - [[package]] name = "memmap2" version = "0.9.5" @@ -416,80 +289,23 @@ dependencies = [ ] [[package]] -name = "mime" -version = "0.3.17" +name = "opaque-debug" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] -name = "miniz_oxide" -version = "0.8.0" +name = "polyval" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" dependencies = [ - "adler2", + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", ] -[[package]] -name = "mio" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" -dependencies = [ - "hermit-abi", - "libc", - "wasi", - "windows-sys", -] - -[[package]] -name = "object" -version = "0.36.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" -dependencies = [ - "portable-atomic", -] - -[[package]] -name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - -[[package]] -name = "pin-project-lite" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" - -[[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.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" - -[[package]] -name = "portable-atomic" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" - [[package]] name = "proc-macro2" version = "1.0.86" @@ -508,6 +324,15 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "regalloc2" version = "0.10.2" @@ -521,26 +346,6 @@ dependencies = [ "smallvec", ] -[[package]] -name = "rusqlite" -version = "0.32.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" -dependencies = [ - "bitflags", - "fallible-iterator", - "fallible-streaming-iterator", - "hashlink", - "libsqlite3-sys", - "smallvec", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" - [[package]] name = "rustc-hash" version = "2.0.0" @@ -548,70 +353,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" [[package]] -name = "rustversion" -version = "1.0.17" +name = "rustc_version" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" - -[[package]] -name = "ryu" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" - -[[package]] -name = "serde" -version = "1.0.210" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "serde_derive", + "semver", ] [[package]] -name = "serde_derive" -version = "1.0.210" +name = "semver" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ - "proc-macro2", - "quote", - "syn", + "cfg-if", + "cpufeatures", + "digest", ] [[package]] -name = "serde_json" -version = "1.0.128" +name = "signature" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" -dependencies = [ - "itoa", - "memchr", - "ryu", - "serde", -] - -[[package]] -name = "serde_path_to_error" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" -dependencies = [ - "itoa", - "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", -] +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" [[package]] name = "smallvec" @@ -620,14 +391,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] -name = "socket2" -version = "0.5.7" +name = "subtle" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" -dependencies = [ - "libc", - "windows-sys", -] +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" @@ -641,90 +408,10 @@ dependencies = [ ] [[package]] -name = "sync_wrapper" -version = "0.1.2" +name = "typenum" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - -[[package]] -name = "sync_wrapper" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" - -[[package]] -name = "tokio" -version = "1.40.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" -dependencies = [ - "backtrace", - "libc", - "mio", - "pin-project-lite", - "socket2", - "tokio-macros", - "windows-sys", -] - -[[package]] -name = "tokio-macros" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tower" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper 0.1.2", - "tokio", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tracing" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" -dependencies = [ - "log", - "pin-project-lite", - "tracing-core", -] - -[[package]] -name = "tracing-core" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -dependencies = [ - "once_cell", -] +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-ident" @@ -733,10 +420,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] -name = "vcpkg" -version = "0.2.15" +name = "universal-hash" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] [[package]] name = "version_check" @@ -751,98 +442,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] -name = "windows-sys" -version = "0.52.0" +name = "x25519-dalek" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" dependencies = [ - "windows-targets", + "curve25519-dalek", + "rand_core", ] -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - [[package]] name = "xtask" version = "0.1.0" - -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] diff --git a/Cargo.toml b/Cargo.toml index 4ffe95b..a76447b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "2" -members = ["hbbytecode", "hbvm", "hbxrt", "xtask", "hblang", "hbjit", "depell", "depell/html-macro"] +members = ["hbbytecode", "hbvm", "hbxrt", "xtask", "hblang", "hbjit", "depell"] [profile.release] lto = true diff --git a/depell/Cargo.toml b/depell/Cargo.toml index e1fef58..b88fab7 100644 --- a/depell/Cargo.toml +++ b/depell/Cargo.toml @@ -4,5 +4,8 @@ version = "0.1.0" edition = "2021" [dependencies] -axum = "0.7.7" -rusqlite = "0.32.1" +aes-gcm = { version = "0.10.3", default-features = false, features = ["aes", "rand_core"] } +ed25519-dalek = { version = "2.1.1", default-features = false, features = ["rand_core"] } +getrandom = "0.2.15" +rand_core = { version = "0.6.4", features = ["getrandom"] } +x25519-dalek = { version = "2.0.1", default-features = false } diff --git a/depell/html-macro/Cargo.toml b/depell/html-macro/Cargo.toml deleted file mode 100644 index 8d16ce2..0000000 --- a/depell/html-macro/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "html-macro" -version = "0.1.0" -edition = "2021" - -[lib] -proc-macro = true diff --git a/depell/html-macro/src/lib.rs b/depell/html-macro/src/lib.rs deleted file mode 100644 index 11c9499..0000000 --- a/depell/html-macro/src/lib.rs +++ /dev/null @@ -1,530 +0,0 @@ -#![feature(proc_macro_diagnostic)] -#![feature(proc_macro_def_site)] - -use std::fmt::Write; -use std::str::FromStr; - -macro_rules! diag { - ($level:ident: $($tt:tt)*) => {diag!($level(proc_macro::Span::call_site()): $($tt)*)}; - ($level:ident[$span:expr]: $($tt:tt)*) => {diag!($level($span.span()): $($tt)*)}; - ($level:ident($span:expr): $($tt:tt)*) => {{ - proc_macro::Diagnostic::spanned($span, proc_macro::Level::$level, format!($($tt)*)).emit(); - }}; -} - -macro_rules! fatal_diag { - ($($tt:tt)*) => {{ - diag!($($tt)*); - return None; - }}; -} - -#[proc_macro] -pub fn html(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let mut trees = input.into_iter().peekable(); - let mut st = State { - final_code: "Html({ let mut ___out = String::new(); ".to_string(), - write_target: "___out".to_string(), - error_handler: ".unwrap()", - ..State::default() - }; - - if st.output_fmt(&mut trees).is_none() { - return proc_macro::TokenStream::default(); - }; - - st.final_code.push_str("___out })"); - - if !st.tag_stack.is_empty() { - diag!(Error(proc_macro::Span::def_site()): "unclosed tags: {}", st.tag_stack); - } - - proc_macro::TokenStream::from_str(&st.final_code).unwrap() -} - -#[proc_macro] -pub fn write_html(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let mut trees = input.into_iter().peekable(); - let mut st = State { - write_target: trees.next().expect("expected first token").to_string(), - error_handler: ".unwrap()", - ..State::default() - }; - - if st.output_fmt(&mut trees).is_none() { - return proc_macro::TokenStream::default(); - }; - - if !st.tag_stack.is_empty() { - diag!(Error(proc_macro::Span::def_site()): "unclosed tags: {}", st.tag_stack); - } - - proc_macro::TokenStream::from_str(&st.final_code).unwrap() -} - -type Trees = std::iter::Peekable; - -#[derive(Default)] -struct State { - final_code: String, - template: String, - tstr: String, - tag_stack: String, - write_target: String, - no_escaping: bool, - error_handler: &'static str, -} - -impl State { - fn flush_template(&mut self) { - if self.template.is_empty() { - return; - } - write!( - self.final_code, - "write!({}, {:?}){};", - self.write_target, self.template, self.error_handler - ) - .unwrap(); - self.template.clear(); - } - - fn display_expr(&mut self, expr: impl std::fmt::Display) { - self.flush_template(); - if std::mem::take(&mut self.no_escaping) { - write!( - self.final_code, - "write!({}, \"{{}}\", {expr}){};", - self.write_target, self.error_handler - ) - .unwrap(); - } else { - write!( - self.final_code, - "write!({}, \"{{}}\", &HtmlEscaped(&{expr})){};", - self.write_target, self.error_handler, - ) - .unwrap() - } - } - - fn expect_group( - &mut self, - trees: &mut Trees, - delim: proc_macro::Delimiter, - ) -> Option { - match trees.next() { - Some(proc_macro::TokenTree::Group(g)) if g.delimiter() == delim => Some(g.stream()), - Some(c) => fatal_diag!(Error[c]: "expected {delim:?} group"), - None => fatal_diag!(Error: "expected {delim:?} group, got eof"), - } - } - - fn expect_punct(&mut self, trees: &mut Trees, c: char) -> Option<()> { - match trees.next() { - Some(proc_macro::TokenTree::Punct(p)) if p.as_char() == c => Some(()), - Some(c) => fatal_diag!(Error[c]: "expected {c}"), - None => fatal_diag!(Error: "expected {c}, got eof"), - } - } - - fn tag(&mut self, p: proc_macro::Punct, trees: &mut Trees) -> Option<()> { - let temp = match trees.next() { - Some(proc_macro::TokenTree::Ident(id)) => { - let temp = temp_str(&id, &mut self.tstr); - if !is_html_tag(temp) { - diag!(Error[p]: "expected html5 tag name"); - } - temp - } - Some(proc_macro::TokenTree::Literal(lit)) => { - let temp = temp_str(&lit, &mut self.tstr).trim_matches('"'); - if is_html_tag(temp) { - if !temp.contains('!') { - diag!(Warning[p]: "unnescessary string escaping"); - } - } else if !is_valid_webcomponent(temp) { - diag!(Error[p]: "invalid web component identifier"); - } - temp - } - Some(proc_macro::TokenTree::Punct(p)) if p.as_char() == '/' => { - let Some((_, top)) = self.tag_stack.rsplit_once(',') else { - fatal_diag!(Error[p]: "no tag to close"); - }; - - let new_tag_stack_len = self.tag_stack.len() - top.len() - 1; - - let (temp, span) = match trees.next() { - Some(proc_macro::TokenTree::Ident(id)) => { - (temp_str(&id, &mut self.tstr), id.span()) - } - Some(proc_macro::TokenTree::Literal(lit)) => { - (temp_str(&lit, &mut self.tstr).trim_matches('"'), lit.span()) - } - Some(proc_macro::TokenTree::Punct(p)) if p.as_char() == '>' => { - // easter egg - write!(&mut self.template, "").unwrap(); - self.tag_stack.truncate(new_tag_stack_len); - return Some(()); - } - Some(c) => fatal_diag!(Error[c]: "unexpected token in closing tag"), - None => { - fatal_diag!(Error[p]: "expected tag ident or string or '>'") - } - }; - - if temp != top { - diag!(Error(span): "expected closing '{top}' tag"); - } - - write!(&mut self.template, "").unwrap(); - self.expect_punct(trees, '>')?; - self.tag_stack.truncate(new_tag_stack_len); - return Some(()); - } - _ => fatal_diag!(Error[p]: "expected tag ident or string literal"), - }; - - write!(&mut self.template, "<{temp}").unwrap(); - if !is_self_closing(temp) { - write!(self.tag_stack, ",{temp}").unwrap(); - } - - let mut has_attr = false; - while let Some(c) = trees.next() { - let mut has_attr_tmp = false; - match c { - proc_macro::TokenTree::Punct(p) if p.as_char() == '=' && has_attr => loop { - match trees.next() { - Some(proc_macro::TokenTree::Punct(p)) if p.as_char() == '!' => { - self.no_escaping = true; - continue; - } - Some(proc_macro::TokenTree::Literal(lit)) => { - write!(&mut self.template, "={lit}").unwrap(); - } - Some(proc_macro::TokenTree::Ident(id)) => { - write!(&mut self.template, "=\"").unwrap(); - self.display_expr(id); - write!(&mut self.template, "\"").unwrap(); - } - Some(proc_macro::TokenTree::Group(g)) - if g.delimiter() == proc_macro::Delimiter::Brace => - { - write!(&mut self.template, "=\"").unwrap(); - self.display_expr(g.stream()); - write!(&mut self.template, "\"").unwrap(); - } - Some(c) => { - diag!(Error[c]: "unexpected token in attr value") - } - None => diag!(Error[p]: "expected attribute value"), - } - break; - }, - proc_macro::TokenTree::Punct(p) if p.as_char() == '>' => { - write!(&mut self.template, ">").unwrap(); - break; - } - proc_macro::TokenTree::Ident(id) => { - write!(&mut self.template, " {id}").unwrap(); - has_attr_tmp = true; - } - proc_macro::TokenTree::Literal(lit) => { - let temp = temp_str(&lit, &mut self.tstr).trim_matches('"'); - if !is_valid_html_attt_name(temp) { - diag!(Error[p]: "invalid attribute name"); - } - write!(&mut self.template, " {temp}").unwrap(); - has_attr_tmp = true; - } - c => diag!(Error[c]: "unexpected token in attribute list"), - } - has_attr = has_attr_tmp; - } - - Some(()) - } - - fn matches_char(t: &proc_macro::TokenTree, ch: char, spacing: proc_macro::Spacing) -> bool { - matches!(t, proc_macro::TokenTree::Punct(p) if p.as_char() == ch && p.spacing() == spacing) - } - - fn match_expr(&mut self, trees: &mut Trees) -> Option<()> { - let expr = self.expect_group(trees, proc_macro::Delimiter::Parenthesis)?; - let mut body = self - .expect_group(trees, proc_macro::Delimiter::Brace)? - .into_iter() - .peekable(); - self.flush_template(); - - write!(&mut self.final_code, "match {expr} {{").unwrap(); - - loop { - let mut pattern = proc_macro::TokenStream::new(); - let mut looped = false; - - while let Some(c) = body.next() { - if !Self::matches_char(&c, '=', proc_macro::Spacing::Joint) { - pattern.extend([c]); - looped = true; - continue; - } - - let nc = body.next().expect("haaaaa"); - if Self::matches_char(&nc, '>', proc_macro::Spacing::Alone) { - break; - } - pattern.extend([c, nc]); - looped = true; - } - - if !looped { - break; - } - - let body = self.expect_group(&mut body, proc_macro::Delimiter::Brace)?; - - write!(&mut self.final_code, "{pattern} => {{").unwrap(); - self.output_fmt(&mut body.into_iter().peekable())?; - write!(&mut self.final_code, "}}").unwrap(); - } - - write!(&mut self.final_code, "}}").unwrap(); - - Some(()) - } - - fn for_expr(&mut self, trees: &mut Trees) -> Option<()> { - let mut ink = proc_macro::Span::call_site(); - let loop_var = trees - .by_ref() - .take_while(|t| { - ink = t.span(); - !matches!(t, proc_macro::TokenTree::Ident(id) if temp_str(id, &mut self.tstr) == "in") - }) - .collect::(); - - let iter = self.expect_group(trees, proc_macro::Delimiter::Parenthesis)?; - let body = self.expect_group(trees, proc_macro::Delimiter::Brace)?; - let else_body = match trees.peek() { - Some(proc_macro::TokenTree::Ident(id)) if temp_str(id, &mut self.tstr) == "else" => { - trees.next(); - self.expect_group(trees, proc_macro::Delimiter::Brace)? - } - _ => proc_macro::TokenStream::new(), - }; - - self.tstr.clear(); - - self.flush_template(); - - if else_body.is_empty() { - write!(&mut self.final_code, "for {loop_var} in {iter} {{").unwrap(); - } else { - write!( - &mut self.final_code, - "let mut looped = false;\ - for {loop_var} in {iter} {{\ - looped = true;" - ) - .unwrap(); - } - self.output_fmt(&mut body.into_iter().peekable())?; - write!(&mut self.final_code, "}}").unwrap(); - if !else_body.is_empty() { - write!(&mut self.final_code, "if !looped {{").unwrap(); - self.output_fmt(&mut else_body.into_iter().peekable())?; - write!(&mut self.final_code, "}}").unwrap(); - } - Some(()) - } - - fn output_fmt(&mut self, trees: &mut Trees) -> Option<()> { - while let Some(c) = trees.next() { - match c { - proc_macro::TokenTree::Punct(p) if p.as_char() == '!' => self.no_escaping = true, - proc_macro::TokenTree::Punct(p) if p.as_char() == '<' => self.tag(p, trees)?, - proc_macro::TokenTree::Literal(lit) => self - .template - .push_str(temp_str(&lit, &mut self.tstr).trim_matches('"')), - proc_macro::TokenTree::Ident(id) => match temp_str(&id, &mut self.tstr) { - "match" => self.match_expr(trees)?, - "for" => self.for_expr(trees)?, - _ => self.display_expr(id), - }, - proc_macro::TokenTree::Group(g) - if g.delimiter() == proc_macro::Delimiter::Brace => - { - self.display_expr(g.stream()); - } - c => fatal_diag!(Error[c]: "unexpected token"), - } - } - self.flush_template(); - - Some(()) - } -} - -fn temp_str(i: impl std::fmt::Display, buf: &mut String) -> &str { - buf.clear(); - write!(buf, "{i}").unwrap(); - buf -} - -fn is_valid_html_attt_name(tag: &str) -> bool { - tag.bytes() - .all(|c| matches!(c, b'a'..=b'z' | b'_' | b'-' | b'A'..=b'Z' | 128..=u8::MAX)) -} - -fn is_valid_webcomponent(tag: &str) -> bool { - let mut seen_dash = false; - tag.bytes() - .inspect(|c| seen_dash |= *c == b'-') - .all(|c| matches!(c, b'a'..=b'z' | b'_' | b'-' | b'.' | 128..=u8::MAX)) - && seen_dash -} - -fn is_html_tag(tag: &str) -> bool { - matches!( - tag, - "!DOCTYPE" - | "a" - | "abbr" - | "acronym" - | "address" - | "area" - | "article" - | "aside" - | "audio" - | "b" - | "base" - | "basefont" - | "bdi" - | "bdo" - | "big" - | "blockquote" - | "body" - | "br" - | "button" - | "canvas" - | "caption" - | "center" - | "cite" - | "code" - | "col" - | "colgroup" - | "data" - | "datalist" - | "dd" - | "del" - | "details" - | "dfn" - | "dialog" - | "div" - | "dl" - | "dt" - | "em" - | "embed" - | "fieldset" - | "figcaption" - | "figure" - | "footer" - | "form" - | "h1" - | "h2" - | "h3" - | "h4" - | "h5" - | "h6" - | "head" - | "header" - | "hr" - | "html" - | "i" - | "iframe" - | "img" - | "input" - | "ins" - | "kbd" - | "label" - | "legend" - | "li" - | "link" - | "main" - | "map" - | "mark" - | "meta" - | "meter" - | "nav" - | "noscript" - | "object" - | "ol" - | "optgroup" - | "option" - | "output" - | "p" - | "param" - | "picture" - | "pre" - | "progress" - | "q" - | "rp" - | "rt" - | "ruby" - | "s" - | "samp" - | "script" - | "section" - | "select" - | "small" - | "source" - | "span" - | "strong" - | "style" - | "sub" - | "summary" - | "sup" - | "svg" - | "table" - | "tbody" - | "td" - | "template" - | "textarea" - | "tfoot" - | "th" - | "thead" - | "time" - | "title" - | "tr" - | "track" - | "u" - | "ul" - | "var" - | "video" - | "wbr" - ) -} - -fn is_self_closing(tag: &str) -> bool { - matches!( - tag, - "!DOCTYPE" - | "area" - | "base" - | "br" - | "col" - | "embed" - | "hr" - | "img" - | "input" - | "link" - | "meta" - | "param" - | "source" - | "track" - | "wbr" - ) -} diff --git a/depell/src/main.rs b/depell/src/main.rs index e7a11a9..2ee3417 100644 --- a/depell/src/main.rs +++ b/depell/src/main.rs @@ -1,3 +1,544 @@ -fn main() { - println!("Hello, world!"); +#![feature(array_windows)] +#![feature(write_all_vectored)] +use { + aes_gcm::{ + aead::{self, AeadMutInPlace}, + AeadCore, Aes256Gcm, KeyInit, + }, + ed25519_dalek::ed25519::signature::Signer, + rand_core::OsRng, + std::{ + collections::{HashMap, HashSet}, + fmt, fs, + io::{self, IoSlice, IoSliceMut, Read, Write}, + mem::{self, MaybeUninit}, + net::{Ipv4Addr, SocketAddrV4, TcpListener, TcpStream}, + path::PathBuf, + slice, + str::FromStr, + sync::{atomic, OnceLock}, + time, + }, + x25519_dalek::{EphemeralSecret, SharedSecret}, +}; + +static CONN_COUNT: atomic::AtomicUsize = atomic::AtomicUsize::new(0); +static USER_DATA_DIR: OnceLock = OnceLock::new(); +static SERVER_SECRET: OnceLock = OnceLock::new(); + +#[derive(Default)] +struct Cli { + program: String, + args: Vec, + flags: HashSet, + options: HashMap, +} + +impl Cli { + pub fn parse() -> Self { + let mut s = Self::default(); + let mut args = std::env::args(); + s.program = args.next().unwrap(); + + for arg in args { + if let Some(arg) = arg.strip_prefix("--") { + match arg.split_once('=') { + Some((name, value)) => _ = s.options.insert(name.to_owned(), value.to_owned()), + None => _ = s.flags.insert(arg.to_string()), + } + } else { + s.args.push(arg); + } + } + + s + } + + pub fn arg(&self, index: usize) -> &str { + self.args.get(index).map_or("", String::as_str) + } + + pub fn expect_option(&self, name: &str) -> &str { + self.options.get(name).unwrap_or_else(|| panic!("--{name} is mandatory")) + } + + pub fn expect_poption>(&self, name: &str) -> T { + self.expect_option(name).parse::().unwrap_or_else(|e| { + panic!("failed to parse --{name} as {}: {e}", std::any::type_name::()) + }) + } +} + +type Subcommand<'a> = (&'a str, &'a str, fn(&Cli) -> io::Result<()>); + +fn help(subs: &[(&str, &str, T)]) -> io::Result<()> { + for (name, desc, _) in subs { + eprintln!("{name} - {desc}"); + } + Err(io::ErrorKind::NotFound.into()) +} + +const SUBCOMMANDS: &[Subcommand] = &[ + ("help", "print command descriptions", |_| help(SUBCOMMANDS)), + ("serve", "run the server", |cli| { + let port = cli.expect_poption::("port"); + let max_conns = cli.expect_poption::("max-conns"); + USER_DATA_DIR.set(cli.expect_poption("user-data-path")).unwrap(); + SERVER_SECRET.set(cli.expect_poption::("secret").0).unwrap(); + let listener = TcpListener::bind((Ipv4Addr::UNSPECIFIED, port)).unwrap(); + for incoming in listener.incoming() { + match incoming { + Ok(c) => { + if CONN_COUNT.fetch_add(1, atomic::Ordering::Relaxed) >= max_conns { + CONN_COUNT.fetch_sub(1, atomic::Ordering::Relaxed); + continue; + } + + std::thread::spawn(move || { + _ = handle_client(c); + CONN_COUNT.fetch_sub(1, atomic::Ordering::Relaxed); + }); + } + Err(e) => { + eprintln!("accepting conn conn: {e}") + } + } + } + Ok(()) + }), + ("make-profile", "create profile file (private key + name)", |cli| { + let name = cli.expect_option("name"); + let name = str_as_username(name) + .ok_or(io::ErrorKind::InvalidData) + .ctx("name is limmited to 32 characters")?; + + let &key = ed25519_dalek::SigningKey::generate(&mut rand_core::OsRng).as_bytes(); + let profile = UserProfile { name, key }; + + let out_file = cli.expect_option("out-file"); + _ = fs::write(out_file, as_bytes(&profile)).ctx("while saving profile file"); + Ok(()) + }), + ("consume", "connect to server and do an action", |cli| { + let profile_path = cli.expect_option("profile"); + let mut profile_file = fs::File::open(profile_path).ctx("opening profile file")?; + let profile: UserProfile = read_struct(&mut profile_file).ctx("reading the profile")?; + let sx = x25519_dalek::EphemeralSecret::random_from_rng(OsRng); + let auth = UserAuth::sign(profile, &sx); + + let addr = cli.expect_poption::("addr"); + let mut stream = TcpStream::connect(addr).ctx("creating connection to the server")?; + write_struct(&mut stream, &auth).ctx("sending initial handshake packet")?; + + let HexPk(server_identity) = cli.expect_poption("server-identity"); + let sauth: ServerAuth = read_struct(&mut stream).ctx("reading server auth")?; + let secret = sauth + .verify(auth.pk, server_identity, sx) + .map_err(|_| io::ErrorKind::PermissionDenied) + .ctx("authenticating server")?; + let stream = EncriptedStream::new(stream, secret); + + select_subcommand(1, CONSUME_SUBCOMMAND, cli)(cli, stream) + }), +]; + +type ConsumeSubcommand<'a> = (&'a str, &'a str, fn(&Cli, EncriptedStream) -> io::Result<()>); + +const CONSUME_SUBCOMMAND: &[ConsumeSubcommand] = &[ + ("help", "this help message", |_, _| help(CONSUME_SUBCOMMAND)), + ("ping", "ping the server to check the connection", |_, mut stream| { + write_struct(&mut stream, &Qid::Ping) + }), // +]; + +fn hex_to_array(s: &str) -> Result<[u8; SIZE], &'static str> { + let mut buf = [0u8; SIZE]; + + if s.len() != SIZE * 2 { + return Err("expected 64 character hex string"); + } + + fn byte_to_hex(val: u8) -> Result { + Ok(match val { + b'0'..=b'9' => val - b'0', + b'a'..=b'f' => val - b'a' + 10, + b'A'..=b'F' => val - b'A' + 10, + _ => return Err("invalid hex char"), + }) + } + + for (dst, &[a, b]) in buf.iter_mut().zip(s.as_bytes().array_windows()) { + *dst = byte_to_hex(a)? | (byte_to_hex(b)? << 4); + } + + Ok(buf) +} + +struct HexPk(ed25519_dalek::VerifyingKey); + +impl std::str::FromStr for HexPk { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + ed25519_dalek::VerifyingKey::from_bytes(&hex_to_array(s)?) + .map_err(|_| "hex code does not represent the valid key") + .map(Self) + } +} + +struct HexSk(ed25519_dalek::SigningKey); + +impl std::str::FromStr for HexSk { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + Ok(Self(ed25519_dalek::SigningKey::from_bytes(&hex_to_array(s)?))) + } +} + +fn select_subcommand<'a, T>(depth: usize, list: &'a [(&str, &str, T)], cli: &Cli) -> &'a T { + &list.iter().find(|&&(name, ..)| name == cli.arg(depth)).unwrap_or(&list[0]).2 +} + +fn main() -> io::Result<()> { + let cli = Cli::parse(); + select_subcommand(0, SUBCOMMANDS, &cli)(&cli) +} + +fn handle_client(mut stream: TcpStream) -> io::Result<()> { + let (user, sec) = { + let user_auth: UserAuth = read_struct(&mut stream).ctx("reading auth packet")?; + let sx = x25519_dalek::EphemeralSecret::random_from_rng(OsRng); + let pk = x25519_dalek::PublicKey::from(&sx); + let user = UserData::load(&user_auth, sx).ctx("loading user data")?; + let sauth = ServerAuth::sign(&user_auth, SERVER_SECRET.get().unwrap(), pk); + write_struct(&mut stream, &sauth).ctx("sending handshare response")?; + user + }; + + let mut stream = EncriptedStream::new(stream, sec); + + loop { + match Qid::try_from(read_struct::(&mut stream)?)? { + Qid::Ping => write_struct(&mut stream, &Aid::Pong)?, + } + } +} + +#[repr(u16)] +enum Aid { + Pong, +} + +#[repr(u16)] +enum Qid { + Ping, +} + +impl TryFrom for Qid { + type Error = io::ErrorKind; + + fn try_from(value: u16) -> Result { + if value <= Self::Ping as u16 { + Ok(unsafe { mem::transmute::(value) }) + } else { + Err(io::ErrorKind::NotFound) + } + } +} + +trait Ctx { + fn ctx(self, label: &str) -> Self; +} + +impl Ctx for Result { + fn ctx(self, label: &str) -> Self { + if let Err(e) = &self { + eprintln!("{label}: {e}") + } + self + } +} + +const ASOC_DATA: &[u8] = b"testicle torsion vizard"; + +struct EncriptedStream { + inner: TcpStream, + key: SharedSecret, + buf: Vec, +} + +impl EncriptedStream { + fn new(inner: TcpStream, key: SharedSecret) -> Self { + Self { inner, key, buf: Default::default() } + } +} + +impl Read for EncriptedStream { + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + let mut tag = MaybeUninit::>::uninit(); + let mut nonce = MaybeUninit::>::uninit(); + + let mut bufs = &mut [ + IoSliceMut::new(as_mut_bytes(&mut tag)), + IoSliceMut::new(as_mut_bytes(&mut nonce)), + IoSliceMut::new(buf), + ][..]; + + loop { + let red = self.inner.read_vectored(bufs)?; + if red == 0 { + return Err(io::ErrorKind::UnexpectedEof.into()); + } + IoSliceMut::advance_slices(&mut bufs, red); + if bufs.is_empty() { + break; + } + } + + unsafe { + Aes256Gcm::new(self.key.as_bytes().into()) + .decrypt_in_place_detached(&nonce.assume_init(), ASOC_DATA, buf, &tag.assume_init()) + .map_err(|_| io::ErrorKind::PermissionDenied)?; + } + + Ok(()) + } + + fn read(&mut self, _: &mut [u8]) -> io::Result { + unimplemented!() + } +} + +impl Write for EncriptedStream { + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + self.buf.clear(); + self.buf.extend(buf); + + let nonce = Aes256Gcm::generate_nonce(OsRng); + let tag = Aes256Gcm::new(self.key.as_bytes().into()) + .encrypt_in_place_detached(&nonce, ASOC_DATA, &mut self.buf) + .unwrap(); + + self.inner.write_all_vectored(&mut [ + IoSlice::new(as_bytes(&tag)), + IoSlice::new(as_bytes(&nonce)), + IoSlice::new(&self.buf), + ]) + } + + fn flush(&mut self) -> io::Result<()> { + self.inner.flush() + } + + fn write(&mut self, _: &[u8]) -> io::Result { + unimplemented!() + } +} + +fn read_struct(stream: &mut impl Read) -> io::Result { + let mut res = mem::MaybeUninit::uninit(); + stream.read_exact(as_mut_bytes(&mut res))?; + unsafe { res.assume_init() } +} + +fn write_struct(stream: &mut impl Write, value: &T) -> io::Result<()> { + stream.write_all(as_bytes(value)) +} + +fn as_mut_bytes(value: &mut T) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(value as *mut _ as *mut u8, mem::size_of::()) } +} + +fn as_bytes(value: &T) -> &[u8] { + unsafe { slice::from_raw_parts(value as *const _ as *const u8, mem::size_of::()) } +} + +type Username = [u8; 32]; +type Postname = [u8; 64]; +type Pk = [u8; 32]; +type Nonce = u64; + +fn username_as_str(name: &Username) -> Option<&str> { + let len = name.iter().rposition(|&b| b != 0xff)? + 1; + std::str::from_utf8(&name[..len]).ok() +} + +fn str_as_username(name: &str) -> Option { + if name.len() > mem::size_of::() { + return None; + } + let mut buff = [0xffu8; mem::size_of::()]; + buff[..name.len()].copy_from_slice(name.as_bytes()); + Some(buff) +} + +#[repr(packed)] +struct UserProfile { + name: Username, + key: ed25519_dalek::SecretKey, +} + +#[repr(packed)] +struct UserAuth { + signature: ed25519_dalek::Signature, + pk: Pk, + x: x25519_dalek::PublicKey, + name: Username, + nonce: Nonce, +} + +impl UserAuth { + fn sign(UserProfile { name, key }: UserProfile, sx: &x25519_dalek::EphemeralSecret) -> Self { + let nonce = + time::SystemTime::now().duration_since(time::SystemTime::UNIX_EPOCH).unwrap().as_secs(); + let mut message = [0; mem::size_of::() + mem::size_of::()]; + message[..mem::size_of::()].copy_from_slice(&name); + message[mem::size_of::()..].copy_from_slice(&nonce.to_le_bytes()); + + let signing_key = ed25519_dalek::SigningKey::from_bytes(&key); + let signature = signing_key.sign(&message); + let pk = ed25519_dalek::VerifyingKey::from(&signing_key).to_bytes(); + let x = x25519_dalek::PublicKey::from(sx); + + Self { signature, pk, x, name, nonce } + } + + fn verify( + &self, + pk: Pk, + nonce: Nonce, + sx: x25519_dalek::EphemeralSecret, + ) -> Result { + if nonce >= self.nonce { + eprintln!("invalid auth nonce"); + return Err(ed25519_dalek::SignatureError::default()); + } + + let pk = ed25519_dalek::VerifyingKey::from_bytes(&pk)?; + + let mut message = [0; mem::size_of::() + mem::size_of::()]; + message[..mem::size_of::()].copy_from_slice(&self.name); + message[mem::size_of::()..].copy_from_slice(&nonce.to_le_bytes()); + + pk.verify_strict(&message, &self.signature)?; + + Ok(sx.diffie_hellman(&self.x)) + } +} + +#[repr(packed)] +struct ServerAuth { + signature: ed25519_dalek::Signature, + x: x25519_dalek::PublicKey, +} + +impl ServerAuth { + fn sign( + user_auth: &UserAuth, + sk: &ed25519_dalek::SigningKey, + x: x25519_dalek::PublicKey, + ) -> Self { + let signature = sk.sign(user_auth.x.as_bytes()); + Self { signature, x } + } + + fn verify( + &self, + x: Pk, + pk: ed25519_dalek::VerifyingKey, + sx: EphemeralSecret, + ) -> Result { + pk.verify_strict(&x, &self.signature)?; + Ok(sx.diffie_hellman(&self.x)) + } +} + +struct UserData { + header: UserHeader, + post_headers: fs::File, + posts: fs::File, +} + +impl UserData { + fn load( + auth: &UserAuth, + sx: x25519_dalek::EphemeralSecret, + ) -> io::Result<(Self, SharedSecret)> { + const HEADER_PATH: &str = "header.bin"; + const POST_HEADERS_PATH: &str = "post-headers.bin"; + const POST_PATH: &str = "posts.bin"; + + let mut path = PathBuf::from_iter([ + USER_DATA_DIR.get().unwrap().as_path(), + username_as_str(&auth.name).ok_or(io::ErrorKind::InvalidData)?.as_ref(), + ]); + + if path.exists() { + path.push(HEADER_PATH); + let mut header_file = fs::File::open(&path).ctx("opening user header file")?; + let mut header: UserHeader = + read_struct(&mut header_file).ctx("reading the user header")?; + path.pop(); + + let secret = auth + .verify(header.pk, header.nonce, sx) + .map_err(|_| io::ErrorKind::PermissionDenied) + .ctx("authenticating user")?; + + header.nonce = auth.nonce; + write_struct(&mut header_file, &header).ctx("saving user nonce")?; + + path.push(POST_HEADERS_PATH); + let post_headers = fs::File::open(&path).ctx("opening user post header file")?; + path.pop(); + + path.push(POST_PATH); + let posts = fs::File::open(&path).ctx("opening user post file")?; + path.pop(); + + Ok((Self { header, post_headers, posts }, secret)) + } else { + let secret = auth + .verify(auth.pk, 0, sx) + .map_err(|_| io::ErrorKind::PermissionDenied) + .ctx("verifiing registratio signature")?; + + fs::create_dir_all(&path).ctx("creating new user directory")?; + path.push(HEADER_PATH); + let header = + UserHeader { pk: auth.pk, nonce: auth.nonce, post_count: 0, runs: 0, imports: 0 }; + fs::write(&path, as_bytes(&header)).ctx("writing new user header")?; + path.pop(); + + path.push(POST_HEADERS_PATH); + let post_headers = fs::File::create_new(&path).ctx("creating new user post headers")?; + path.pop(); + + path.push(POST_PATH); + let posts = fs::File::create_new(&path).ctx("creating new user posts")?; + path.pop(); + + Ok((Self { header, post_headers, posts }, secret)) + } + } +} + +#[repr(packed)] +struct UserHeader { + pk: Pk, + nonce: Nonce, + post_count: u32, + imports: u32, + runs: u32, +} + +#[repr(packed)] +struct PostHeader { + name: Postname, + timestamp: u64, + size: u32, + offset: u32, + imports: u32, + runs: u32, }