server: remove username and password support (#383)

* remove username and password support

* remove sqlx dependency
This commit is contained in:
Conrado Gouvea 2024-12-26 16:42:12 -03:00 committed by GitHub
parent fe7ea0a27d
commit 2a07585b85
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 187 additions and 1372 deletions

643
Cargo.lock generated
View File

@ -52,18 +52,6 @@ dependencies = [
"subtle",
]
[[package]]
name = "ahash"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
"zerocopy 0.7.35",
]
[[package]]
name = "aho-corasick"
version = "1.1.3"
@ -73,12 +61,6 @@ dependencies = [
"memchr",
]
[[package]]
name = "allocator-api2"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
[[package]]
name = "anstream"
version = "0.6.15"
@ -134,18 +116,6 @@ version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
[[package]]
name = "argon2"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
dependencies = [
"base64ct",
"blake2",
"cpufeatures",
"password-hash",
]
[[package]]
name = "arrayref"
version = "0.3.8"
@ -179,15 +149,6 @@ dependencies = [
"syn 2.0.87",
]
[[package]]
name = "atoi"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528"
dependencies = [
"num-traits",
]
[[package]]
name = "atomic-polyfill"
version = "1.0.3"
@ -391,9 +352,6 @@ name = "bitflags"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
dependencies = [
"serde",
]
[[package]]
name = "bitvec"
@ -570,15 +528,6 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
[[package]]
name = "concurrent-queue"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "const-crc32-nostd"
version = "1.3.1"
@ -661,21 +610,6 @@ dependencies = [
"libc",
]
[[package]]
name = "crc"
version = "3.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636"
dependencies = [
"crc-catalog",
]
[[package]]
name = "crc-catalog"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
[[package]]
name = "critical-section"
version = "1.1.2"
@ -691,15 +625,6 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-queue"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.20"
@ -784,7 +709,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0"
dependencies = [
"const-oid",
"pem-rfc7468",
"zeroize",
]
@ -843,7 +767,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"const-oid",
"crypto-common",
"subtle",
]
@ -905,12 +828,6 @@ dependencies = [
"litrs",
]
[[package]]
name = "dotenvy"
version = "0.15.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
[[package]]
name = "ed25519"
version = "2.2.3"
@ -940,9 +857,6 @@ name = "either"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
dependencies = [
"serde",
]
[[package]]
name = "embedded-io"
@ -981,28 +895,6 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "etcetera"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943"
dependencies = [
"cfg-if",
"home",
"windows-sys 0.48.0",
]
[[package]]
name = "event-listener"
version = "5.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba"
dependencies = [
"concurrent-queue",
"parking",
"pin-project-lite",
]
[[package]]
name = "exitcode"
version = "1.1.2"
@ -1041,17 +933,6 @@ version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
[[package]]
name = "flume"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
dependencies = [
"futures-core",
"futures-sink",
"spin",
]
[[package]]
name = "fnv"
version = "1.0.7"
@ -1213,17 +1094,6 @@ dependencies = [
"futures-util",
]
[[package]]
name = "futures-intrusive"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f"
dependencies = [
"futures-core",
"lock_api",
"parking_lot",
]
[[package]]
name = "futures-io"
version = "0.3.31"
@ -1288,10 +1158,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi",
"wasm-bindgen",
]
[[package]]
@ -1354,19 +1222,6 @@ name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
dependencies = [
"ahash",
"allocator-api2",
]
[[package]]
name = "hashlink"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
dependencies = [
"hashbrown",
]
[[package]]
name = "headers"
@ -1433,33 +1288,6 @@ dependencies = [
"serde",
]
[[package]]
name = "hkdf"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7"
dependencies = [
"hmac",
]
[[package]]
name = "hmac"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
dependencies = [
"digest",
]
[[package]]
name = "home"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "http"
version = "0.2.12"
@ -1687,9 +1515,6 @@ name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
dependencies = [
"spin",
]
[[package]]
name = "libc"
@ -1697,12 +1522,6 @@ version = "0.2.164"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f"
[[package]]
name = "libm"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
[[package]]
name = "libredox"
version = "0.1.3"
@ -1713,17 +1532,6 @@ dependencies = [
"libc",
]
[[package]]
name = "libsqlite3-sys"
version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149"
dependencies = [
"cc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "linux-raw-sys"
version = "0.4.14"
@ -1767,16 +1575,6 @@ version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
[[package]]
name = "md-5"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
dependencies = [
"cfg-if",
"digest",
]
[[package]]
name = "memchr"
version = "2.7.4"
@ -1829,12 +1627,6 @@ dependencies = [
"unicase",
]
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.7.4"
@ -1914,16 +1706,6 @@ dependencies = [
"memoffset",
]
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
@ -1934,59 +1716,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "num-bigint-dig"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151"
dependencies = [
"byteorder",
"lazy_static",
"libm",
"num-integer",
"num-iter",
"num-traits",
"rand",
"smallvec",
"zeroize",
]
[[package]]
name = "num-conv"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "num-integer"
version = "0.1.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
dependencies = [
"num-traits",
]
[[package]]
name = "num-iter"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
"libm",
]
[[package]]
name = "object"
version = "0.32.2"
@ -2064,12 +1799,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "parking"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae"
[[package]]
name = "parking_lot"
version = "0.12.3"
@ -2088,7 +1817,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
dependencies = [
"cfg-if",
"libc",
"redox_syscall 0.5.3",
"redox_syscall",
"smallvec",
"windows-targets 0.52.6",
]
@ -2119,29 +1848,6 @@ dependencies = [
"xeddsa",
]
[[package]]
name = "password-auth"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a2a4764cc1f8d961d802af27193c6f4f0124bd0e76e8393cf818e18880f0524"
dependencies = [
"argon2",
"getrandom",
"password-hash",
"rand_core",
]
[[package]]
name = "password-hash"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
dependencies = [
"base64ct",
"rand_core",
"subtle",
]
[[package]]
name = "pasta_curves"
version = "0.5.1"
@ -2155,21 +1861,6 @@ dependencies = [
"subtle",
]
[[package]]
name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "pem-rfc7468"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
dependencies = [
"base64ct",
]
[[package]]
name = "percent-encoding"
version = "2.3.1"
@ -2217,17 +1908,6 @@ dependencies = [
"crossbeam-channel",
]
[[package]]
name = "pkcs1"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f"
dependencies = [
"der",
"pkcs8",
"spki",
]
[[package]]
name = "pkcs8"
version = "0.10.2"
@ -2292,7 +1972,7 @@ version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dee4364d9f3b902ef14fab8a1ddffb783a1cb6b4bba3bfc1fa3922732c7de97f"
dependencies = [
"zerocopy 0.6.6",
"zerocopy",
]
[[package]]
@ -2377,15 +2057,6 @@ dependencies = [
"zeroize",
]
[[package]]
name = "redox_syscall"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "redox_syscall"
version = "0.5.3"
@ -2529,26 +2200,6 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "rsa"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc"
dependencies = [
"const-oid",
"digest",
"num-bigint-dig",
"num-integer",
"num-traits",
"pkcs1",
"pkcs8",
"rand_core",
"signature",
"spki",
"subtle",
"zeroize",
]
[[package]]
name = "rtoolbox"
version = "0.0.2"
@ -2789,7 +2440,6 @@ dependencies = [
"frost-ed25519",
"frost-rerandomized",
"hex",
"password-auth",
"rand",
"reddsa",
"regex",
@ -2798,7 +2448,6 @@ dependencies = [
"serde_json",
"serdect",
"snow",
"sqlx",
"tokio",
"tower-http",
"tracing",
@ -2853,7 +2502,6 @@ version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
dependencies = [
"digest",
"rand_core",
]
@ -2871,9 +2519,6 @@ name = "smallvec"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
dependencies = [
"serde",
]
[[package]]
name = "snow"
@ -2920,218 +2565,6 @@ dependencies = [
"der",
]
[[package]]
name = "sqlformat"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f895e3734318cc55f1fe66258926c9b910c124d47520339efecbb6c59cec7c1f"
dependencies = [
"nom",
"unicode_categories",
]
[[package]]
name = "sqlx"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93334716a037193fac19df402f8571269c84a00852f6a7066b5d2616dcd64d3e"
dependencies = [
"sqlx-core",
"sqlx-macros",
"sqlx-mysql",
"sqlx-postgres",
"sqlx-sqlite",
]
[[package]]
name = "sqlx-core"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4d8060b456358185f7d50c55d9b5066ad956956fddec42ee2e8567134a8936e"
dependencies = [
"atoi",
"byteorder",
"bytes",
"crc",
"crossbeam-queue",
"either",
"event-listener",
"futures-channel",
"futures-core",
"futures-intrusive",
"futures-io",
"futures-util",
"hashbrown",
"hashlink",
"hex",
"indexmap",
"log",
"memchr",
"once_cell",
"paste",
"percent-encoding",
"serde",
"serde_json",
"sha2",
"smallvec",
"sqlformat",
"thiserror 1.0.69",
"time",
"tokio",
"tokio-stream",
"tracing",
"url",
"uuid",
]
[[package]]
name = "sqlx-macros"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cac0692bcc9de3b073e8d747391827297e075c7710ff6276d9f7a1f3d58c6657"
dependencies = [
"proc-macro2",
"quote",
"sqlx-core",
"sqlx-macros-core",
"syn 2.0.87",
]
[[package]]
name = "sqlx-macros-core"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1804e8a7c7865599c9c79be146dc8a9fd8cc86935fa641d3ea58e5f0688abaa5"
dependencies = [
"dotenvy",
"either",
"heck 0.5.0",
"hex",
"once_cell",
"proc-macro2",
"quote",
"serde",
"serde_json",
"sha2",
"sqlx-core",
"sqlx-mysql",
"sqlx-postgres",
"sqlx-sqlite",
"syn 2.0.87",
"tempfile",
"tokio",
"url",
]
[[package]]
name = "sqlx-mysql"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64bb4714269afa44aef2755150a0fc19d756fb580a67db8885608cf02f47d06a"
dependencies = [
"atoi",
"base64 0.22.1",
"bitflags 2.6.0",
"byteorder",
"bytes",
"crc",
"digest",
"dotenvy",
"either",
"futures-channel",
"futures-core",
"futures-io",
"futures-util",
"generic-array",
"hex",
"hkdf",
"hmac",
"itoa",
"log",
"md-5",
"memchr",
"once_cell",
"percent-encoding",
"rand",
"rsa",
"serde",
"sha1",
"sha2",
"smallvec",
"sqlx-core",
"stringprep",
"thiserror 1.0.69",
"time",
"tracing",
"uuid",
"whoami",
]
[[package]]
name = "sqlx-postgres"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fa91a732d854c5d7726349bb4bb879bb9478993ceb764247660aee25f67c2f8"
dependencies = [
"atoi",
"base64 0.22.1",
"bitflags 2.6.0",
"byteorder",
"crc",
"dotenvy",
"etcetera",
"futures-channel",
"futures-core",
"futures-io",
"futures-util",
"hex",
"hkdf",
"hmac",
"home",
"itoa",
"log",
"md-5",
"memchr",
"once_cell",
"rand",
"serde",
"serde_json",
"sha2",
"smallvec",
"sqlx-core",
"stringprep",
"thiserror 1.0.69",
"time",
"tracing",
"uuid",
"whoami",
]
[[package]]
name = "sqlx-sqlite"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5b2cf34a45953bfd3daaf3db0f7a7878ab9b7a6b91b422d24a7a9e4c857b680"
dependencies = [
"atoi",
"flume",
"futures-channel",
"futures-core",
"futures-executor",
"futures-intrusive",
"futures-util",
"libsqlite3-sys",
"log",
"percent-encoding",
"serde",
"serde_urlencoded",
"sqlx-core",
"time",
"tracing",
"url",
"uuid",
]
[[package]]
name = "stable-eyre"
version = "0.2.2"
@ -3155,17 +2588,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "stringprep"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1"
dependencies = [
"unicode-bidi",
"unicode-normalization",
"unicode-properties",
]
[[package]]
name = "strsim"
version = "0.11.1"
@ -3461,17 +2883,6 @@ dependencies = [
"tokio",
]
[[package]]
name = "tokio-stream"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af"
dependencies = [
"futures-core",
"pin-project-lite",
"tokio",
]
[[package]]
name = "tokio-util"
version = "0.7.11"
@ -3719,18 +3130,6 @@ dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-properties"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4259d9d4425d9f0661581b804cb85fe66a4c631cadd8f490d1c13a35d5d9291"
[[package]]
name = "unicode_categories"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
[[package]]
name = "universal-hash"
version = "0.5.1"
@ -3825,12 +3224,6 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasite"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
[[package]]
name = "wasm-bindgen"
version = "0.2.92"
@ -3907,16 +3300,6 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "whoami"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9"
dependencies = [
"redox_syscall 0.4.1",
"wasite",
]
[[package]]
name = "winapi"
version = "0.3.9"
@ -4176,16 +3559,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "854e949ac82d619ee9a14c66a1b674ac730422372ccb759ce0c39cabcf2bf8e6"
dependencies = [
"byteorder",
"zerocopy-derive 0.6.6",
]
[[package]]
name = "zerocopy"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
"zerocopy-derive 0.7.35",
"zerocopy-derive",
]
[[package]]
@ -4199,17 +3573,6 @@ dependencies = [
"syn 2.0.87",
]
[[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 2.0.87",
]
[[package]]
name = "zeroize"
version = "1.8.1"

View File

@ -31,15 +31,6 @@ pub struct Args {
#[arg(long, default_value_t = false)]
pub http: bool,
/// The username to use in HTTP mode.
#[arg(short = 'u', long, default_value = "")]
pub username: String,
/// The password to use in HTTP mode. If specified, it will be read from the
/// environment variable with the given name.
#[arg(short = 'w', long, default_value = "")]
pub password: String,
/// The comma-separated usernames of the signers to use in HTTP mode.
/// If HTTP mode is enabled and this is empty, then the session ID
/// will be printed and will have to be shared manually.
@ -99,16 +90,6 @@ pub struct ProcessedArgs<C: Ciphersuite> {
/// FROST server.
pub http: bool,
/// The username to use in HTTP mode.
pub username: String,
/// The (actual) password to use in HTTP mode.
pub password: String,
/// The authentication token to use in HTTP mode; if not specified
/// it will login with `password`
pub authentication_token: Option<String>,
/// The comma-separated keys of the signers to use in
/// HTTP mode. If HTTP mode is enabled and this is empty, then the session
/// ID will be printed and will have to be shared manually.
@ -138,15 +119,15 @@ pub struct ProcessedArgs<C: Ciphersuite> {
/// Port to connect to, if using HTTP mode.
pub port: u16,
/// The coordinator's communication private key. Specifying this along with
/// `comm_participant_pubkey_getter` enables encryption.
/// The coordinator's communication private key for HTTP mode.
pub comm_privkey: Option<Vec<u8>>,
/// The coordinator's communication public key.
/// The coordinator's communication public key for HTTP mode.
pub comm_pubkey: Option<Vec<u8>>,
/// A function that confirms if the public key of a participant is in the
/// user's contact book, returning the same public key, or None if not.
/// user's contact book, returning the same public key, or None if not. For
/// HTTP mode.
// It is a `Rc<dyn Fn>` to make it easier to use;
// using `fn()` would preclude using closures and using generics would
// require a lot of code change for something simple.
@ -163,12 +144,6 @@ impl<C: Ciphersuite + 'static> ProcessedArgs<C> {
input: &mut dyn BufRead,
output: &mut dyn Write,
) -> Result<Self, Box<dyn Error>> {
let password = if args.http {
read_password(&args.password)?
} else {
String::new()
};
let num_signers = if !args.signers.is_empty() {
args.signers.len() as u16
} else if args.num_signers == 0 {
@ -204,8 +179,6 @@ impl<C: Ciphersuite + 'static> ProcessedArgs<C> {
Ok(ProcessedArgs {
cli: args.cli,
http: args.http,
username: args.username.clone(),
password,
signers,
num_signers,
public_key_package,
@ -214,7 +187,6 @@ impl<C: Ciphersuite + 'static> ProcessedArgs<C> {
signature: args.signature.clone(),
ip: args.ip.clone(),
port: args.port,
authentication_token: None,
comm_privkey: None,
comm_pubkey: None,
comm_participant_pubkey_getter: None,

View File

@ -266,12 +266,11 @@ pub struct HTTPComms<C: Ciphersuite> {
client: reqwest::Client,
host_port: String,
session_id: Option<Uuid>,
access_token: String,
access_token: Option<String>,
num_signers: u16,
args: ProcessedArgs<C>,
state: SessionState<C>,
pubkeys: HashMap<Vec<u8>, Identifier<C>>,
should_logout: bool,
// The "send" Noise objects by pubkey of recipients.
send_noise: Option<HashMap<Vec<u8>, Noise>>,
// The "receive" Noise objects by pubkey of senders.
@ -286,12 +285,11 @@ impl<C: Ciphersuite> HTTPComms<C> {
client,
host_port: format!("http://{}:{}", args.ip, args.port),
session_id: None,
access_token: args.authentication_token.clone().unwrap_or_default(),
access_token: None,
num_signers: 0,
args: args.clone(),
state: SessionState::new(args.messages.len(), args.num_signers as usize),
pubkeys: Default::default(),
should_logout: args.authentication_token.is_none(),
send_noise: None,
recv_noise: None,
_phantom: Default::default(),
@ -370,29 +368,30 @@ impl<C: Ciphersuite + 'static> Comms<C> for HTTPComms<C> {
);
let signature: [u8; 64] = privkey.sign(challenge.as_bytes(), &mut rng);
self.access_token = self
.client
.post(format!("{}/key_login", self.host_port))
.json(&server::KeyLoginArgs {
uuid: challenge,
pubkey: self
.args
.comm_pubkey
.clone()
.ok_or_eyre("comm_pubkey must be specified")?,
signature: signature.to_vec(),
})
.send()
.await?
.json::<server::LoginOutput>()
.await?
.access_token
.to_string();
self.access_token = Some(
self.client
.post(format!("{}/login", self.host_port))
.json(&server::KeyLoginArgs {
uuid: challenge,
pubkey: self
.args
.comm_pubkey
.clone()
.ok_or_eyre("comm_pubkey must be specified")?,
signature: signature.to_vec(),
})
.send()
.await?
.json::<server::LoginOutput>()
.await?
.access_token
.to_string(),
);
let r = self
.client
.post(format!("{}/create_new_session", self.host_port))
.bearer_auth(&self.access_token)
.bearer_auth(self.access_token.as_ref().expect("was just set"))
.json(&server::CreateNewSessionArgs {
pubkeys: self.args.signers.iter().cloned().map(PublicKey).collect(),
num_signers,
@ -460,7 +459,7 @@ impl<C: Ciphersuite + 'static> Comms<C> for HTTPComms<C> {
let r = self
.client
.post(format!("{}/receive", self.host_port))
.bearer_auth(&self.access_token)
.bearer_auth(self.access_token.as_ref().expect("was just set"))
.json(&server::ReceiveArgs {
session_id: r.session_id,
as_coordinator: true,
@ -511,7 +510,11 @@ impl<C: Ciphersuite + 'static> Comms<C> for HTTPComms<C> {
let _r = self
.client
.post(format!("{}/send", self.host_port))
.bearer_auth(&self.access_token)
.bearer_auth(
self.access_token
.as_ref()
.expect("must have been set before"),
)
.json(&server::SendArgs {
session_id: self.session_id.unwrap(),
recipients: vec![server::PublicKey(recipient.clone())],
@ -529,7 +532,11 @@ impl<C: Ciphersuite + 'static> Comms<C> for HTTPComms<C> {
let r = self
.client
.post(format!("{}/receive", self.host_port))
.bearer_auth(&self.access_token)
.bearer_auth(
self.access_token
.as_ref()
.expect("must have been set before"),
)
.json(&server::ReceiveArgs {
session_id: self.session_id.unwrap(),
as_coordinator: true,
@ -553,21 +560,27 @@ impl<C: Ciphersuite + 'static> Comms<C> for HTTPComms<C> {
let _r = self
.client
.post(format!("{}/close_session", self.host_port))
.bearer_auth(&self.access_token)
.bearer_auth(
self.access_token
.as_ref()
.expect("must have been set before"),
)
.json(&server::CloseSessionArgs {
session_id: self.session_id.unwrap(),
})
.send()
.await?;
if self.should_logout {
let _r = self
.client
.post(format!("{}/logout", self.host_port))
.bearer_auth(&self.access_token)
.send()
.await?;
}
let _r = self
.client
.post(format!("{}/logout", self.host_port))
.bearer_auth(
self.access_token
.as_ref()
.expect("must have been set before"),
)
.send()
.await?;
let signature_shares = self.state.signature_shares()?;

View File

@ -9,31 +9,9 @@ pub(crate) struct Args {
#[derive(Subcommand, Clone)]
pub(crate) enum Command {
/// Initializes the user, generating a communication key pair and optionally
/// registering with a FROST server. The key pair and additional information
/// are saved to the config file. You can rerun the command to register
/// in other servers; the communication key pair will not be regenerated.
Init {
/// The username to use when registering, if desired.
#[arg(short, long)]
username: Option<String>,
/// The server URL to use, if desired.
#[arg(short, long)]
server_url: Option<String>,
/// The path to the config file to manage. If not specified, it uses
/// $HOME/.local/frost/credentials.toml
#[arg(short, long)]
config: Option<String>,
},
/// Logs the user on the server and saves the returned authentication token
/// Initializes the user, generating a communication key pair and saving
/// to the config file.
Login {
/// The username to use when logging in.
#[arg(short, long)]
username: String,
/// The server URL to use.
#[arg(short, long)]
server_url: String,
Init {
/// The path to the config file to manage. If not specified, it uses
/// $HOME/.local/frost/credentials.toml
#[arg(short, long)]
@ -45,10 +23,6 @@ pub(crate) enum Command {
/// The name to use when exporting.
#[arg(short, long)]
name: String,
/// The server URL for which to export a contact. You can use a
/// substring of the URL.
#[arg(short, long)]
server_url: Option<String>,
/// The path to the config file to manage. If not specified, it uses
/// $HOME/.local/frost/credentials.toml
#[arg(short, long)]
@ -86,10 +60,6 @@ pub(crate) enum Command {
/// The comma-separated name of each participant.
#[arg(short = 'N', long, value_delimiter = ',')]
names: Vec<String>,
/// The comma-separated username of each participant in the same order
/// as `names`. Note: these won't be checked in the server.
#[arg(short, long, value_delimiter = ',')]
usernames: Vec<String>,
/// The server URL, if desired. Note that this does not connect to the
/// server; it will just associated the server URL with the group in the
/// config file.

View File

@ -18,10 +18,6 @@ pub struct Config {
#[serde(skip)]
path: Option<PathBuf>,
pub version: u8,
/// The registry of servers the user has registered into, keyed by server
/// URL.
#[serde(default)]
pub registry: BTreeMap<String, Registry>,
/// The communication key pair for the user.
pub communication_key: Option<CommunicationKey>,
/// The address book of the user, keyed by each contact's name.
@ -41,25 +37,6 @@ impl Config {
.cloned()
.ok_or_eyre("contact not found")?)
}
pub fn username_by_server_url(&self, server_url: &str) -> Result<String, Box<dyn Error>> {
Ok(self
.registry
.get(server_url)
.ok_or_eyre("Not logged in in the giver server")?
.username
.clone())
}
}
/// A registry entry. Note that the server URL is not in the struct;
/// it is the key in the `registry` map in Config.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Registry {
/// The authentication token, if the user is logged in.
pub token: Option<String>,
/// The username of the user
pub username: String,
}
/// The communication key pair for the user.
@ -117,7 +94,7 @@ impl Group {
);
for participant in self.participant.values() {
let contact = config.contact_by_pubkey(&participant.pubkey)?;
s += &format!("\t{}\n", contact.name);
s += &format!("\t{} ({})\n", contact.name, hex::encode(contact.pubkey));
}
Ok(s)
}
@ -138,8 +115,6 @@ pub struct Participant {
deserialize_with = "serdect::slice::deserialize_hex_or_bin_vec"
)]
pub pubkey: Vec<u8>,
/// The username of the participant in the server, if any.
pub username: Option<String>,
}
impl Config {

View File

@ -20,28 +20,17 @@ pub struct Contact {
deserialize_with = "serdect::slice::deserialize_hex_or_bin_vec"
)]
pub pubkey: Vec<u8>,
/// The URL of the server where the contact is registered, if any.
pub server_url: Option<String>,
/// The username of the contact on `server_url`, if registered.
pub username: Option<String>,
}
impl Contact {
/// Returns a human-readable summary of the contact; used when it is
/// printed to the terminal.
pub fn as_human_readable_summary(&self) -> String {
let mut s = format!(
format!(
"Name: {}\nPublic Key: {}\n",
self.name,
hex::encode(&self.pubkey)
);
if let Some(server_url) = &self.server_url {
s += format!("Server URL: {}\n", server_url).as_str();
}
if let Some(username) = &self.username {
s += format!("Username: {}\n", username).as_str();
}
s
)
}
/// Returns the contact encoded as a text string, with Bech32.
@ -92,57 +81,12 @@ pub(crate) fn import(args: &Command) -> Result<(), Box<dyn Error>> {
/// Export a contact from the user's address book in the config file.
pub(crate) fn export(args: &Command) -> Result<(), Box<dyn Error>> {
let Command::Export {
name,
server_url,
config,
} = (*args).clone()
else {
let Command::Export { name, config } = (*args).clone() else {
panic!("invalid Command");
};
let config = Config::read(config)?;
// Get the server_url to export depending on whether the user has registered
// in a server, or if they are registered in multiple servers.
let server_url = if config.registry.is_empty() && server_url.is_some() {
return Err(eyre!("User has not been registered yet").into());
} else if config.registry.is_empty() {
None
} else if config.registry.len() > 1 {
let Some(server_url) = &server_url else {
return Err(eyre!(
"More than one registry found. Specify which one with the server_url argument"
)
.into());
};
// There are multiple server registrations. Try to match one using
// `server_url` with a simple substring test.
let matches: Vec<_> = config
.registry
.keys()
.filter(|k| k.contains(server_url))
.collect();
if matches.is_empty() {
return Err(eyre!("server_url not found").into());
} else if matches.len() > 1 {
return Err(eyre!(
"Multiple registries matches the server_url argument; make it more specific"
)
.into());
}
Some(matches[0].clone())
} else {
Some(
config
.registry
.first_key_value()
.expect("should have an entry")
.0
.clone(),
)
};
// Build the contact to export.
let contact = Contact {
version: Some(0),
@ -151,8 +95,6 @@ pub(crate) fn export(args: &Command) -> Result<(), Box<dyn Error>> {
.communication_key
.ok_or(eyre!("pubkey not generated yet"))?
.pubkey,
server_url: server_url.clone(),
username: server_url.map(|s| config.registry[&s].username.clone()),
};
eprintln!("Exporting this information:");

View File

@ -76,8 +76,6 @@ pub(crate) async fn run_for_ciphersuite<C: RandomizedCiphersuite + 'static>(
let pargs = coordinator::args::ProcessedArgs {
cli: false,
http: true,
username: String::new(),
password: String::new(),
signers,
num_signers,
public_key_package,
@ -89,7 +87,6 @@ pub(crate) async fn run_for_ciphersuite<C: RandomizedCiphersuite + 'static>(
.ok_or_eyre("host missing in URL")?
.to_owned(),
port: server_url_parsed.port().unwrap_or(2744),
authentication_token: None,
comm_privkey: Some(
config
.communication_key

View File

@ -1,91 +1,28 @@
use std::error::Error;
use eyre::eyre;
use crate::{
args::Command,
config::{CommunicationKey, Config, Registry},
config::{CommunicationKey, Config},
};
pub(crate) async fn init(args: &Command) -> Result<(), Box<dyn Error>> {
let Command::Init {
server_url,
username,
config,
} = (*args).clone()
else {
let Command::Init { config } = (*args).clone() else {
panic!("invalid Command");
};
let mut config = Config::read(config)?;
let pubkey = match &config.communication_key {
Some(communication_key) => {
eprintln!("Skipping keypair generation; keypair already generated and stored");
hex::decode(&communication_key.pubkey)?
}
None => {
eprintln!("Generating keypair... ");
let builder = snow::Builder::new("Noise_K_25519_ChaChaPoly_BLAKE2s".parse().unwrap());
let keypair = builder.generate_keypair().unwrap();
config.communication_key = Some(CommunicationKey {
privkey: keypair.private.clone(),
pubkey: keypair.public.clone(),
});
keypair.public
}
};
if let (Some(server_url), Some(username)) = (server_url, username) {
// TODO: check if already registered, prompt to overwrite
let client = reqwest::Client::new();
let password = rpassword::prompt_password("Password to use: ").unwrap();
let rpassword = rpassword::prompt_password("Repeat password: ").unwrap();
if password != rpassword {
return Err(eyre!("Passwords are different").into());
}
eprintln!("Registering at {}...", server_url);
let r = client
.post(format!("http://{}/register", server_url))
.json(&server::RegisterArgs {
username: username.clone(),
password: password.clone(),
pubkey: pubkey.clone(),
})
.send()
.await?;
if r.status() != reqwest::StatusCode::OK {
return Err(eyre!("{}", r.text().await?).into());
}
eprintln!("Logging in at {}...", server_url);
let r = client
.post(format!("http://{}/login", server_url))
.json(&server::LoginArgs {
username: username.clone(),
password,
})
.send()
.await?;
if r.status() != reqwest::StatusCode::OK {
return Err(eyre!("{}", r.text().await?).into());
}
let r = r.json::<server::LoginOutput>().await?;
config.registry.insert(
server_url,
Registry {
token: Some(r.access_token.to_string()),
username,
},
);
if config.communication_key.is_some() {
eprintln!("Skipping keypair generation; keypair already generated and stored");
} else {
eprintln!(
"Skipping user registration, specify username and server_url if you want to register"
);
}
eprintln!("Generating keypair... ");
let builder = snow::Builder::new("Noise_K_25519_ChaChaPoly_BLAKE2s".parse().unwrap());
let keypair = builder.generate_keypair().unwrap();
config.communication_key = Some(CommunicationKey {
privkey: keypair.private.clone(),
pubkey: keypair.public.clone(),
});
};
eprintln!(
"Writing to config file at {}...",

View File

@ -1,55 +0,0 @@
use std::error::Error;
use eyre::eyre;
use crate::{
args::Command,
config::{Config, Registry},
};
pub(crate) async fn login(args: &Command) -> Result<(), Box<dyn Error>> {
let Command::Login {
server_url,
username,
config,
} = (*args).clone()
else {
panic!("invalid Command");
};
let mut config = Config::read(config)?;
let client = reqwest::Client::new();
let password = rpassword::prompt_password("Password: ").unwrap();
eprintln!("Logging in at {}...", server_url);
let r = client
.post(format!("http://{}/login", server_url))
.json(&server::LoginArgs {
username: username.clone(),
password,
})
.send()
.await?;
if r.status() != reqwest::StatusCode::OK {
return Err(eyre!("{}", r.text().await?).into());
}
let r = r.json::<server::LoginOutput>().await?;
config.registry.insert(
server_url,
Registry {
token: Some(r.access_token.to_string()),
username,
},
);
eprintln!(
"Writing to config file at {}...",
config.path().expect("should not be None").display()
);
config.write()?;
eprintln!("Done.");
Ok(())
}

View File

@ -5,7 +5,6 @@ pub mod contact;
pub mod coordinator;
pub mod group;
pub mod init;
pub mod login;
pub mod participant;
pub mod trusted_dealer;
pub mod write_atomic;
@ -22,7 +21,6 @@ async fn main() -> Result<(), Box<dyn Error>> {
match args.command {
Command::Init { .. } => init::init(&args.command).await,
Command::Login { .. } => login::login(&args.command).await,
Command::Export { .. } => contact::export(&args.command),
Command::Import { .. } => contact::import(&args.command),
Command::Contacts { .. } => contact::list(&args.command),

View File

@ -66,15 +66,12 @@ pub(crate) async fn run_for_ciphersuite<C: RandomizedCiphersuite + 'static>(
let pargs = participant::args::ProcessedArgs {
cli: false,
http: true,
username: String::new(),
password: String::new(),
key_package,
ip: server_url_parsed
.host_str()
.ok_or_eyre("host missing in URL")?
.to_owned(),
port: server_url_parsed.port().unwrap_or(2744),
authentication_token: None,
session_id: String::new(),
comm_privkey: Some(
config

View File

@ -37,7 +37,6 @@ pub(crate) fn trusted_dealer_for_ciphersuite<C: Ciphersuite + MaybeIntoEvenY + '
threshold,
num_signers,
names,
usernames,
server_url,
} = (*args).clone()
else {
@ -67,36 +66,21 @@ pub(crate) fn trusted_dealer_for_ciphersuite<C: Ciphersuite + MaybeIntoEvenY + '
// First pass over configs; create participants map
let mut participants = BTreeMap::new();
let mut contacts = Vec::new();
for (idx, (identifier, path, name)) in
izip!(shares.keys(), config.iter(), names.iter()).enumerate()
{
for (identifier, path, name) in izip!(shares.keys(), config.iter(), names.iter()) {
let config = Config::read(Some(path.to_string()))?;
let pubkey = config
.communication_key
.ok_or_eyre("config not initialized")?
.pubkey;
let username = if server_url.is_some() {
Some(
usernames
.get(idx)
.ok_or_eyre("must specify usernames of all users")?
.clone(),
)
} else {
None
};
let participant = Participant {
identifier: identifier.serialize(),
pubkey: pubkey.clone(),
username,
};
participants.insert(hex::encode(identifier.serialize()), participant);
let contact = Contact {
version: None,
name: name.clone(),
pubkey,
server_url: None,
username: None,
};
contacts.push(contact);
}

View File

@ -31,15 +31,6 @@ pub struct Args {
#[arg(long, default_value_t = false)]
pub http: bool,
/// The username to use in HTTP mode.
#[arg(short = 'u', long, default_value = "")]
pub username: String,
/// The password to use in HTTP mode. If specified, it will be read from the
/// environment variable with the given name.
#[arg(short = 'w', long, default_value = "")]
pub password: String,
/// Public key package to use. Can be a file with a JSON-encoded
/// package, or "". If the file does not exist or if "" is specified,
/// then it will be read from standard input.
@ -70,16 +61,6 @@ pub struct ProcessedArgs<C: Ciphersuite> {
/// FROST server.
pub http: bool,
/// The username to use in HTTP mode.
pub username: String,
/// The (actual) password to use in HTTP mode.
pub password: String,
/// The authentication token to use in HTTP mode; if not specified
/// it will login with `password`
pub authentication_token: Option<String>,
/// Key package to use.
pub key_package: KeyPackage<C>,
@ -94,15 +75,14 @@ pub struct ProcessedArgs<C: Ciphersuite> {
/// Optional Session ID
pub session_id: String,
/// The participant's communication private key. Specifying this along with
/// `comm_coordinator_pubkey_getter` enables encryption.
/// The participant's communication private key for HTTP mode.
pub comm_privkey: Option<Vec<u8>>,
/// The participant's communication public key.
/// The participant's communication public key for HTTP mode.
pub comm_pubkey: Option<Vec<u8>>,
/// A function that confirms that a public key from the server is trusted by
/// the user; returns the same public key.
/// the user; returns the same public key. For HTTP mode.
// It is a `Rc<dyn Fn>` to make it easier to use;
// using `fn()` would preclude using closures and using generics would
// require a lot of code change for something simple.
@ -119,12 +99,6 @@ impl<C: Ciphersuite + 'static> ProcessedArgs<C> {
input: &mut dyn BufRead,
output: &mut dyn Write,
) -> Result<Self, Box<dyn Error>> {
let password = if args.http {
read_password(&args.password)?
} else {
String::new()
};
let bytes = read_from_file_or_stdin(input, output, "key package", &args.key_package)?;
let key_package = if let Ok(secret_share) = serde_json::from_str::<SecretShare<C>>(&bytes) {
@ -137,12 +111,9 @@ impl<C: Ciphersuite + 'static> ProcessedArgs<C> {
Ok(ProcessedArgs {
cli: args.cli,
http: args.http,
username: args.username.clone(),
password,
key_package,
ip: args.ip.clone(),
port: args.port,
authentication_token: None,
session_id: args.session_id.clone(),
comm_privkey: None,
comm_pubkey: None,

View File

@ -104,8 +104,7 @@ pub struct HTTPComms<C: Ciphersuite> {
client: reqwest::Client,
host_port: String,
session_id: Option<Uuid>,
access_token: String,
should_logout: bool,
access_token: Option<String>,
args: ProcessedArgs<C>,
send_noise: Option<Noise>,
recv_noise: Option<Noise>,
@ -125,8 +124,7 @@ where
client,
host_port: format!("http://{}:{}", args.ip, args.port),
session_id: Uuid::parse_str(&args.session_id).ok(),
access_token: args.authentication_token.clone().unwrap_or_default(),
should_logout: args.authentication_token.is_none(),
access_token: None,
args: args.clone(),
send_noise: None,
recv_noise: None,
@ -201,24 +199,25 @@ where
);
let signature: [u8; 64] = privkey.sign(challenge.as_bytes(), &mut rng);
self.access_token = self
.client
.post(format!("{}/key_login", self.host_port))
.json(&server::KeyLoginArgs {
uuid: challenge,
pubkey: self
.args
.comm_pubkey
.clone()
.ok_or_eyre("comm_pubkey must be specified")?,
signature: signature.to_vec(),
})
.send()
.await?
.json::<server::LoginOutput>()
.await?
.access_token
.to_string();
self.access_token = Some(
self.client
.post(format!("{}/login", self.host_port))
.json(&server::KeyLoginArgs {
uuid: challenge,
pubkey: self
.args
.comm_pubkey
.clone()
.ok_or_eyre("comm_pubkey must be specified")?,
signature: signature.to_vec(),
})
.send()
.await?
.json::<server::LoginOutput>()
.await?
.access_token
.to_string(),
);
let session_id = match self.session_id {
Some(s) => s,
@ -227,7 +226,7 @@ where
let r = self
.client
.post(format!("{}/list_sessions", self.host_port))
.bearer_auth(&self.access_token)
.bearer_auth(self.access_token.as_ref().expect("was just set"))
.send()
.await?
.json::<server::ListSessionsOutput>()
@ -256,7 +255,7 @@ where
.client
.post(format!("{}/get_session_info", self.host_port))
.json(&server::GetSessionInfoArgs { session_id })
.bearer_auth(&self.access_token)
.bearer_auth(self.access_token.as_ref().expect("was just set"))
.send()
.await?
.json::<server::GetSessionInfoOutput>()
@ -298,7 +297,7 @@ where
let msg = self.encrypt_if_needed(serde_json::to_vec(&send_commitments_args)?)?;
self.client
.post(format!("{}/send", self.host_port))
.bearer_auth(&self.access_token)
.bearer_auth(self.access_token.as_ref().expect("was just set"))
.json(&server::SendArgs {
session_id,
// Empty recipients: Coordinator
@ -316,7 +315,7 @@ where
let r = self
.client
.post(format!("{}/receive", self.host_port))
.bearer_auth(&self.access_token)
.bearer_auth(self.access_token.as_ref().expect("was just set"))
.json(&server::ReceiveArgs {
session_id,
as_coordinator: false,
@ -371,7 +370,7 @@ where
let _r = self
.client
.post(format!("{}/send", self.host_port))
.bearer_auth(&self.access_token)
.bearer_auth(self.access_token.as_ref().expect("must be set before"))
.json(&server::SendArgs {
session_id: self.session_id.unwrap(),
// Empty recipients: Coordinator
@ -381,14 +380,12 @@ where
.send()
.await?;
if self.should_logout {
let _r = self
.client
.post(format!("{}/logout", self.host_port))
.bearer_auth(&self.access_token)
.send()
.await?;
}
let _r = self
.client
.post(format!("{}/logout", self.host_port))
.bearer_auth(self.access_token.as_ref().expect("must be set before"))
.send()
.await?;
Ok(())
}

View File

@ -46,8 +46,6 @@ async fn check_valid_round_1_inputs() {
port: 80,
session_id: "session-id".to_string(),
http: false,
username: "".to_string(),
password: "".to_string(),
};
let input = SECRET_SHARE_JSON;
let mut valid_input = input.as_bytes();

View File

@ -15,13 +15,11 @@ derivative = "2.2.0"
eyre = "0.6.11"
frost-core = { version = "2.0.0", features = ["serde"] }
frost-rerandomized = { version = "2.0.0-rc.0", features = ["serde"] }
password-auth = "1.0.0"
rand = "0.8"
serde = { version = "1.0", features = ["derive"] }
serdect = { version = "0.2.0" }
serde_json = "1.0.133"
snow = "0.9.6"
sqlx = { version = "0.8.2", features = ["sqlite", "time", "runtime-tokio", "uuid"] }
tokio = { version = "1.42", features = ["full"] }
tower-http = { version = "0.6.2", features = ["trace"] }
tracing = "0.1"

View File

@ -10,8 +10,4 @@ pub struct Args {
/// Port to bind to
#[arg(short, long, default_value_t = 2744)]
pub port: u16,
/// Database to use.
#[arg(short, long, default_value = "db.sqlite")]
pub database: String,
}

View File

@ -6,35 +6,10 @@ use xeddsa::{xed25519, Verify as _};
use crate::{
state::{Session, SharedState},
types::*,
user::{
add_access_token, authenticate_user, create_user, delete_user, remove_access_token, User,
},
user::User,
AppError,
};
/// Implement the register API.
#[tracing::instrument(ret, err(Debug), skip(state,args), fields(args.username = %args.username))]
pub(crate) async fn register(
State(state): State<SharedState>,
Json(args): Json<RegisterArgs>,
) -> Result<Json<()>, AppError> {
let username = args.username.trim();
let password = args.password.trim();
if username.is_empty() || password.is_empty() {
return Err(AppError(
StatusCode::INTERNAL_SERVER_ERROR,
eyre!("empty args").into(),
));
}
create_user(state.db.clone(), username, password, args.pubkey)
.await
.map_err(|e| AppError(StatusCode::INTERNAL_SERVER_ERROR, e))?;
Ok(Json(()))
}
/// Implement the challenge API.
#[tracing::instrument(ret, err(Debug), skip(state, _args))]
pub(crate) async fn challenge(
@ -52,7 +27,7 @@ pub(crate) async fn challenge(
/// Implement the key_login API.
#[tracing::instrument(ret, err(Debug), skip(state, args))]
pub(crate) async fn key_login(
pub(crate) async fn login(
State(state): State<SharedState>,
Json(args): Json<KeyLoginArgs>,
) -> Result<Json<KeyLoginOutput>, AppError> {
@ -100,81 +75,22 @@ pub(crate) async fn key_login(
Ok(Json(token))
}
/// Implement the login API.
#[tracing::instrument(ret, err(Debug), skip(state,args), fields(args.username = %args.username))]
pub(crate) async fn login(
State(state): State<SharedState>,
Json(args): Json<LoginArgs>,
) -> Result<Json<LoginOutput>, AppError> {
// Check if the user sent the credentials
if args.username.is_empty() || args.password.is_empty() {
return Err(AppError(
StatusCode::INTERNAL_SERVER_ERROR,
eyre!("empty args").into(),
));
}
let user = authenticate_user(state.db.clone(), &args.username, &args.password)
.await
.map_err(|e| AppError(StatusCode::INTERNAL_SERVER_ERROR, e))?;
let user = match user {
Some(user) => user,
None => {
return Err(AppError(
StatusCode::UNAUTHORIZED,
eyre!("invalid user or password").into(),
))
}
};
let access_token = add_access_token(state.db.clone(), user.id)
.await
.map_err(|e| AppError(StatusCode::INTERNAL_SERVER_ERROR, e))?;
let token = LoginOutput { access_token };
Ok(Json(token))
}
/// Implement the logout API.
#[tracing::instrument(ret, err(Debug), skip(state,user), fields(user.username = %user.username))]
#[tracing::instrument(ret, err(Debug), skip(state, user))]
pub(crate) async fn logout(
State(state): State<SharedState>,
user: User,
) -> Result<Json<()>, AppError> {
state.access_tokens.write().unwrap().remove(
&user
.current_token
.expect("user is logged in so they must have a token"),
);
remove_access_token(
state.db.clone(),
user.current_token
.expect("user is logged in so they must have a token"),
)
.await
.map_err(|e| AppError(StatusCode::INTERNAL_SERVER_ERROR, e))?;
Ok(Json(()))
}
/// Implement the unregister API.
#[tracing::instrument(ret, err(Debug), skip(state,user), fields(user.username = %user.username))]
pub(crate) async fn unregister(
State(state): State<SharedState>,
user: User,
) -> Result<Json<()>, AppError> {
delete_user(state.db.clone(), user.id)
.await
.map_err(|e| AppError(StatusCode::INTERNAL_SERVER_ERROR, e))?;
state
.access_tokens
.write()
.unwrap()
.remove(&user.current_token);
Ok(Json(()))
}
/// Implement the create_new_session API.
#[tracing::instrument(ret, err(Debug), skip(state,user), fields(user.username = %user.username))]
#[tracing::instrument(ret, err(Debug), skip(state, user))]
pub(crate) async fn create_new_session(
State(state): State<SharedState>,
user: User,
@ -216,7 +132,7 @@ pub(crate) async fn create_new_session(
}
/// Implement the create_new_session API.
#[tracing::instrument(ret, err(Debug), skip(state,user), fields(user.username = %user.username))]
#[tracing::instrument(ret, err(Debug), skip(state, user))]
pub(crate) async fn list_sessions(
State(state): State<SharedState>,
user: User,
@ -233,7 +149,7 @@ pub(crate) async fn list_sessions(
}
/// Implement the get_session_info API
#[tracing::instrument(ret, err(Debug), skip(state,user), fields(user.username = %user.username))]
#[tracing::instrument(ret, err(Debug), skip(state, user))]
pub(crate) async fn get_session_info(
State(state): State<SharedState>,
user: User,
@ -241,6 +157,21 @@ pub(crate) async fn get_session_info(
) -> Result<Json<GetSessionInfoOutput>, AppError> {
let state_lock = state.sessions.read().unwrap();
let sessions = state_lock
.sessions_by_pubkey
.get(&user.pubkey)
.ok_or(AppError(
StatusCode::NOT_FOUND,
eyre!("user is not in any session").into(),
))?;
if !sessions.contains(&args.session_id) {
return Err(AppError(
StatusCode::NOT_FOUND,
eyre!("session ID not found").into(),
));
}
let session = state_lock.sessions.get(&args.session_id).ok_or(AppError(
StatusCode::NOT_FOUND,
eyre!("session ID not found").into(),
@ -256,7 +187,7 @@ pub(crate) async fn get_session_info(
/// Implement the send API
// TODO: get identifier from channel rather from arguments
#[tracing::instrument(ret, err(Debug), skip(state,user), fields(user.username = %user.username))]
#[tracing::instrument(ret, err(Debug), skip(state, user))]
pub(crate) async fn send(
State(state): State<SharedState>,
user: User,
@ -294,7 +225,7 @@ pub(crate) async fn send(
/// Implement the recv API
// TODO: get identifier from channel rather from arguments
#[tracing::instrument(ret, err(Debug), skip(state,user), fields(user.username = %user.username))]
#[tracing::instrument(ret, err(Debug), skip(state, user))]
pub(crate) async fn receive(
State(state): State<SharedState>,
user: User,
@ -323,7 +254,7 @@ pub(crate) async fn receive(
}
/// Implement the close_session API.
#[tracing::instrument(ret, err(Debug), skip(state,user), fields(user.username = %user.username))]
#[tracing::instrument(ret, err(Debug), skip(state, user))]
pub(crate) async fn close_session(
State(state): State<SharedState>,
user: User,
@ -331,16 +262,31 @@ pub(crate) async fn close_session(
) -> Result<Json<()>, AppError> {
let mut state = state.sessions.write().unwrap();
for username in state
.sessions
.get(&args.session_id)
.ok_or(AppError(
StatusCode::INTERNAL_SERVER_ERROR,
eyre!("invalid session ID").into(),
))?
.pubkeys
.clone()
{
let sessions = state.sessions_by_pubkey.get(&user.pubkey).ok_or(AppError(
StatusCode::NOT_FOUND,
eyre!("user is not in any session").into(),
))?;
if !sessions.contains(&args.session_id) {
return Err(AppError(
StatusCode::NOT_FOUND,
eyre!("session ID not found").into(),
));
}
let session = state.sessions.get(&args.session_id).ok_or(AppError(
StatusCode::INTERNAL_SERVER_ERROR,
eyre!("invalid session ID").into(),
))?;
if session.coordinator_pubkey != user.pubkey {
return Err(AppError(
StatusCode::NOT_FOUND,
eyre!("user is not the coordinator of the session").into(),
));
}
for username in session.pubkeys.clone() {
if let Some(v) = state.sessions_by_pubkey.get_mut(&username) {
v.remove(&args.session_id);
}

View File

@ -22,12 +22,9 @@ use axum::{
pub fn router(shared_state: SharedState) -> Router {
// Shared state that is passed to each handler by axum
Router::new()
.route("/register", post(functions::register))
.route("/challenge", post(functions::challenge))
.route("/login", post(functions::login))
.route("/key_login", post(functions::key_login))
.route("/logout", post(functions::logout))
.route("/unregister", post(functions::unregister))
.route("/create_new_session", post(functions::create_new_session))
.route("/list_sessions", post(functions::list_sessions))
.route("/get_session_info", post(functions::get_session_info))
@ -40,7 +37,7 @@ pub fn router(shared_state: SharedState) -> Router {
/// Run the server with the specified arguments.
pub async fn run(args: &Args) -> Result<(), Box<dyn std::error::Error>> {
let shared_state = AppState::new(&args.database).await?;
let shared_state = AppState::new().await?;
let app = router(shared_state.clone());
let addr = format!("{}:{}", args.ip, args.port);

View File

@ -1,18 +1,18 @@
use std::{
collections::{HashMap, HashSet, VecDeque},
str::FromStr,
sync::{Arc, RwLock},
};
use delay_map::{HashMapDelay, HashSetDelay};
use sqlx::{
sqlite::{SqliteConnectOptions, SqlitePoolOptions},
SqlitePool,
};
use uuid::Uuid;
use crate::Msg;
/// How long a challenge can be replied to.
const CHALLENGE_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10);
/// How long an acesss token lasts.
const ACCESS_TOKEN_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(60 * 60);
/// A particular signing session.
#[derive(Debug)]
pub struct Session {
@ -36,7 +36,6 @@ pub struct AppState {
pub(crate) sessions: Arc<RwLock<SessionState>>,
pub(crate) challenges: Arc<RwLock<HashSetDelay<Uuid>>>,
pub(crate) access_tokens: Arc<RwLock<HashMapDelay<Uuid, Vec<u8>>>>,
pub(crate) db: SqlitePool,
}
#[derive(Debug, Default)]
@ -47,22 +46,15 @@ pub struct SessionState {
}
impl AppState {
pub async fn new(database: &str) -> Result<SharedState, Box<dyn std::error::Error>> {
tracing::event!(tracing::Level::INFO, "opening database {}", database);
let options = SqliteConnectOptions::from_str(database)?.create_if_missing(true);
let db = SqlitePoolOptions::new().connect_with(options).await?;
sqlx::migrate!().run(&db).await?;
pub async fn new() -> Result<SharedState, Box<dyn std::error::Error>> {
let state = Self {
sessions: Default::default(),
challenges: RwLock::new(HashSetDelay::new(std::time::Duration::from_secs(10))).into(),
access_tokens: RwLock::new(HashMapDelay::new(std::time::Duration::from_secs(60 * 60)))
.into(),
db,
challenges: RwLock::new(HashSetDelay::new(CHALLENGE_TIMEOUT)).into(),
access_tokens: RwLock::new(HashMapDelay::new(ACCESS_TOKEN_TIMEOUT)).into(),
};
Ok(Arc::new(state))
}
}
/// Type alias for the global state under a reference-counted RW mutex,
/// which allows reading and writing the state across different handlers.
/// Type alias for the global state under a reference-counted pointer.
pub type SharedState = Arc<AppState>;

View File

@ -11,172 +11,16 @@ use axum_extra::{
TypedHeader,
};
use eyre::eyre;
use sqlx::{FromRow, SqlitePool};
use tokio::task;
use uuid::Uuid;
use crate::{state::SharedState, AppError};
/// An User, as stored in the database.
#[derive(Debug, FromRow)]
/// An User
#[derive(Debug)]
#[allow(dead_code)]
pub struct User {
pub(crate) id: i64,
pub(crate) username: String,
pub(crate) password: String,
pub(crate) pubkey: Vec<u8>,
#[sqlx(skip)]
pub(crate) access_tokens: Vec<AccessToken>,
#[sqlx(skip)]
pub(crate) current_token: Option<Uuid>,
}
#[derive(Debug, FromRow)]
#[allow(dead_code)]
pub struct AccessToken {
pub(crate) id: i64,
pub(crate) user_id: i64,
pub(crate) access_token: Option<Uuid>,
}
/// Create user in the database.
///
/// The password is hashed and its hash is written in the DB.
pub(crate) async fn create_user(
db: SqlitePool,
username: &str,
password: &str,
pubkey: Vec<u8>,
) -> Result<(), Box<dyn std::error::Error>> {
// TODO: enforce mininum password length
let password = password.to_owned();
let pwhash = task::spawn_blocking(|| password_auth::generate_hash(password)).await?;
sqlx::query(
r#"
insert into users (username, password, pubkey)
values (?, ?, ?)
"#,
)
.bind(username)
.bind(pwhash)
.bind(pubkey)
.execute(&db)
.await?;
Ok(())
}
/// Get user from database, or None if it's not registered.
pub(crate) async fn get_user(
db: SqlitePool,
username: &str,
) -> Result<Option<User>, Box<dyn std::error::Error>> {
let user: Option<User> = sqlx::query_as("select * from users where username = ? ")
.bind(username)
.fetch_optional(&db)
.await?;
if let Some(mut user) = user {
let access_tokens: Vec<AccessToken> =
sqlx::query_as("select * from access_tokens where user_id = ?")
.bind(user.id)
.fetch_all(&db)
.await?;
user.access_tokens = access_tokens;
Ok(Some(user))
} else {
Ok(None)
}
}
/// Delete an User from the database, given its database ID.
pub(crate) async fn delete_user(db: SqlitePool, id: i64) -> Result<(), Box<dyn std::error::Error>> {
sqlx::query(
r#"
delete from users where id = ?
"#,
)
.bind(id)
.execute(&db)
.await?;
Ok(())
}
/// Authenticate user registered in the database. Returns the User if
/// authentication is successful, or None if the username or password is wrong.
///
/// The given password is hashed and verified against the stored hash.
pub(crate) async fn authenticate_user(
db: SqlitePool,
username: &str,
password: &str,
) -> Result<Option<User>, Box<dyn std::error::Error>> {
let user: Option<User> = get_user(db, username).await?;
// Verifying the password is blocking and potentially slow, so we'll do so
// via `spawn_blocking`.
let password = password.to_owned();
let r: Result<_, password_auth::VerifyError> = task::spawn_blocking(|| {
// We're using password-based authentication--this works by comparing our form
// input with an argon2 password hash.
Ok(user.filter(|user| password_auth::verify_password(password, &user.password).is_ok()))
})
.await?;
Ok(r?)
}
/// Refreshes the user's access token, identified by its id in the database.
///
/// Generates a new token and overwrites the old one in the database, if any.
pub(crate) async fn add_access_token(
db: SqlitePool,
id: i64,
) -> Result<Uuid, Box<dyn std::error::Error>> {
let access_token = Uuid::new_v4();
sqlx::query(
r#"
insert into access_tokens (user_id, access_token)
values (?, ?)
"#,
)
.bind(id)
.bind(access_token)
.execute(&db)
.await?;
Ok(access_token)
}
/// Remove a user's access token.
pub(crate) async fn remove_access_token(
db: SqlitePool,
access_token: Uuid,
) -> Result<(), Box<dyn std::error::Error>> {
sqlx::query(
r#"
delete from access_tokens where access_token = ?
"#,
)
.bind(access_token)
.execute(&db)
.await?;
Ok(())
}
/// Return the User for a given access token, or None if there is no match.
pub(crate) async fn get_user_for_access_token(
db: SqlitePool,
access_token: Uuid,
) -> Result<Option<User>, Box<dyn std::error::Error>> {
let user: Option<User> = sqlx::query_as(
r#"
select * from users inner join access_tokens on users.id = access_tokens.user_id where access_tokens.access_token = ?
"#,
)
.bind(access_token)
.fetch_optional(&db)
.await?;
Ok(user)
pub(crate) current_token: Uuid,
}
/// Read a User from a request. This is used to authenticate users. If any axum
@ -219,32 +63,16 @@ impl FromRequestParts<SharedState> for User {
.get(&access_token)
.cloned();
let user = if let Some(pubkey) = pubkey {
Some(User {
id: -1,
username: String::new(),
password: String::new(),
if let Some(pubkey) = pubkey {
Ok(User {
pubkey,
access_tokens: vec![],
current_token: Some(access_token),
current_token: access_token,
})
} else {
get_user_for_access_token(state.db.clone(), access_token)
.await
.map_err(|e| AppError(StatusCode::INTERNAL_SERVER_ERROR, e))?
};
match user {
Some(mut user) => {
user.current_token = Some(access_token);
Ok(user)
}
None => {
return Err(AppError(
StatusCode::INTERNAL_SERVER_ERROR,
eyre!("user not found").into(),
))
}
return Err(AppError(
StatusCode::INTERNAL_SERVER_ERROR,
eyre!("user not found").into(),
));
}
}
}

View File

@ -53,7 +53,7 @@ async fn test_main_router<
.collect();
// Instantiate test server using axum_test
let shared_state = AppState::new(":memory:").await?;
let shared_state = AppState::new().await?;
let router = router(shared_state);
let server = TestServer::new(router)?;
@ -85,7 +85,7 @@ async fn test_main_router<
xed25519::PrivateKey::from(&TryInto::<[u8; 32]>::try_into(alice_keypair.private).unwrap());
let alice_signature: [u8; 64] = alice_private.sign(alice_challenge.as_bytes(), &mut rng);
let res = server
.post("/key_login")
.post("/login")
.json(&server::KeyLoginArgs {
uuid: alice_challenge,
pubkey: alice_keypair.public.clone(),
@ -100,7 +100,7 @@ async fn test_main_router<
xed25519::PrivateKey::from(&TryInto::<[u8; 32]>::try_into(bob_keypair.private).unwrap());
let bob_signature: [u8; 64] = bob_private.sign(bob_challenge.as_bytes(), &mut rng);
let res = server
.post("/key_login")
.post("/login")
.json(&server::KeyLoginArgs {
uuid: bob_challenge,
pubkey: bob_keypair.public.clone(),
@ -389,7 +389,6 @@ async fn test_http() -> Result<(), Box<dyn std::error::Error>> {
// Spawn server for testing
tokio::spawn(async move {
server::run(&Args {
database: ":memory:".to_string(),
ip: "127.0.0.1".to_string(),
port: 2744,
})
@ -425,7 +424,7 @@ async fn test_http() -> Result<(), Box<dyn std::error::Error>> {
xed25519::PrivateKey::from(&TryInto::<[u8; 32]>::try_into(alice_keypair.private).unwrap());
let alice_signature: [u8; 64] = alice_private.sign(alice_challenge.as_bytes(), &mut rng);
let r = client
.post("http://127.0.0.1:2744/key_login")
.post("http://127.0.0.1:2744/login")
.json(&server::KeyLoginArgs {
uuid: alice_challenge,
pubkey: alice_keypair.public.clone(),