From 271445ff07ae4fb5a05878adc0eeb4eb64f046c2 Mon Sep 17 00:00:00 2001 From: Fabian Weik Date: Sat, 24 Jan 2026 20:04:21 +0100 Subject: [PATCH 01/26] read version info when establishing a new connection --- server/src/main.rs | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/server/src/main.rs b/server/src/main.rs index 38665f4..7dae366 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -21,17 +21,9 @@ async fn main() -> Result<(), Error> { let cli_options = CliOptions::parse(); - let version_info = - VersionInfo::get_from_device().context("could not get version information")?; - - info!( - "got version information: hardware {:?}, firmware {}", - version_info.hardware, version_info.firmware, - ); - info!("setting up TCP server"); - let mut server = Server::new(cli_options, version_info) + let mut server = Server::new(cli_options) .await .context("could not create new server")?; @@ -43,19 +35,15 @@ async fn main() -> Result<(), Error> { #[derive(Debug)] pub struct Server { listener: TcpListener, - version_info: VersionInfo, } impl Server { - async fn new(cli_options: CliOptions, version_info: VersionInfo) -> Result { + async fn new(cli_options: CliOptions) -> Result { let listener = TcpListener::bind(&format!("0.0.0.0:{}", cli_options.port)) .await .context(format!("could not bind to port {}", cli_options.port))?; - Ok(Self { - listener, - version_info, - }) + Ok(Self { listener }) } async fn run(&mut self) -> Result<(), Error> { @@ -69,16 +57,23 @@ impl Server { info!("new connection from {}", addr); let conn = Connection::new(stream); - let version_info = self.version_info; spawn(async move { - if let Err(error) = Self::task(conn, version_info).await { + if let Err(error) = Self::task(conn).await { error!("connection terminated with error {}", error); } }); } } - async fn task(mut conn: Connection, version_info: VersionInfo) -> Result<(), Error> { + async fn task(mut conn: Connection) -> Result<(), Error> { + let version_info = + VersionInfo::get_from_device().context("could not get version information")?; + + info!( + "got version information: hardware {:?}, firmware {}", + version_info.hardware, version_info.firmware, + ); + info!("sending out version information"); conn.send_version_info(version_info) From 2f65690804d5d49cd62fe3b85e4d5c5232d9cd59 Mon Sep 17 00:00:00 2001 From: Fabian Weik Date: Sat, 24 Jan 2026 21:03:06 +0100 Subject: [PATCH 02/26] attempt to send public key for auth --- .gitignore | 2 + Cargo.lock | 1033 ++---------------------------- client/Cargo.toml | 2 +- client/src/config.rs | 12 +- client/src/connection/mod.rs | 66 +- client/src/connection/ssh/mod.rs | 32 + client/src/main.rs | 26 +- server/Cargo.toml | 1 + server/src/connection/mod.rs | 60 +- server/src/connection/ssh/mod.rs | 18 + server/src/main.rs | 24 +- 11 files changed, 229 insertions(+), 1047 deletions(-) create mode 100644 client/src/connection/ssh/mod.rs create mode 100644 server/src/connection/ssh/mod.rs diff --git a/.gitignore b/.gitignore index 224c1f1..7a9e878 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ review-client review-server.arm.static + +review.log diff --git a/Cargo.lock b/Cargo.lock index d6caf13..07aeca5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,41 +8,6 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" -[[package]] -name = "aead" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" -dependencies = [ - "crypto-common", - "generic-array 0.14.7", -] - -[[package]] -name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if", - "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]] name = "ahash" version = "0.8.12" @@ -50,7 +15,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", - "const-random", "getrandom 0.3.4", "once_cell", "version_check", @@ -140,31 +104,6 @@ dependencies = [ "num-traits", ] -[[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 = "async-ssh2-tokio" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5413179dd42bcd858cdfd2e13637c81e8b2c15203c90ccacb3c37cfba0f7a68c" -dependencies = [ - "log", - "russh", - "russh-sftp", - "thiserror 2.0.17", - "tokio", -] - [[package]] name = "atomic_refcell" version = "0.1.13" @@ -177,29 +116,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" -[[package]] -name = "aws-lc-rs" -version = "1.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e84ce723ab67259cfeb9877c6a639ee9eb7a27b28123abd71db7f0d5d0cc9d86" -dependencies = [ - "aws-lc-sys", - "untrusted", - "zeroize", -] - -[[package]] -name = "aws-lc-sys" -version = "0.36.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a442ece363113bd4bd4c8b18977a7798dd4d3c3383f34fb61936960e8f4ad8" -dependencies = [ - "cc", - "cmake", - "dunce", - "fs_extra", -] - [[package]] name = "base16ct" version = "0.2.0" @@ -218,17 +134,6 @@ version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" -[[package]] -name = "bcrypt-pbkdf" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aeac2e1fe888769f34f05ac343bbef98b14d1ffb292ab69d4608b3abc86f2a2" -dependencies = [ - "blowfish", - "pbkdf2", - "sha2", -] - [[package]] name = "bitflags" version = "1.3.2" @@ -240,9 +145,6 @@ name = "bitflags" version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" -dependencies = [ - "serde_core", -] [[package]] name = "bitvec" @@ -256,41 +158,13 @@ dependencies = [ "wyz", ] -[[package]] -name = "blake2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" -dependencies = [ - "digest", -] - [[package]] name = "block-buffer" version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "block-padding" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "blowfish" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" -dependencies = [ - "byteorder", - "cipher", + "generic-array", ] [[package]] @@ -333,15 +207,6 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" -[[package]] -name = "cbc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" -dependencies = [ - "cipher", -] - [[package]] name = "cc" version = "1.2.52" @@ -349,8 +214,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3" dependencies = [ "find-msvc-tools", - "jobserver", - "libc", "shlex", ] @@ -370,12 +233,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - [[package]] name = "cgmath" version = "0.18.0" @@ -386,17 +243,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "chacha20" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - [[package]] name = "chrono" version = "0.4.43" @@ -404,9 +250,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" dependencies = [ "iana-time-zone", - "js-sys", "num-traits", - "wasm-bindgen", "windows-link", ] @@ -460,15 +304,6 @@ version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" -[[package]] -name = "cmake" -version = "0.1.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" -dependencies = [ - "cc", -] - [[package]] name = "colorchoice" version = "1.0.4" @@ -481,43 +316,12 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" -[[package]] -name = "const-random" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" -dependencies = [ - "const-random-macro", -] - -[[package]] -name = "const-random-macro" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" -dependencies = [ - "getrandom 0.2.17", - "once_cell", - "tiny-keccak", -] - [[package]] name = "core-foundation-sys" version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" -[[package]] -name = "core-models" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0940496e5c83c54f3b753d5317daec82e8edac71c33aaa1f666d76f518de2444" -dependencies = [ - "hax-lib", - "pastey 0.1.1", - "rand 0.9.2", -] - [[package]] name = "cpufeatures" version = "0.2.17" @@ -536,19 +340,13 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "crunchy" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" - [[package]] name = "crypto-bigint" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ - "generic-array 0.14.7", + "generic-array", "rand_core 0.6.4", "subtle", "zeroize", @@ -560,19 +358,10 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ - "generic-array 0.14.7", + "generic-array", "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" @@ -586,7 +375,6 @@ dependencies = [ "fiat-crypto", "rustc_version", "subtle", - "zeroize", ] [[package]] @@ -600,23 +388,6 @@ dependencies = [ "syn", ] -[[package]] -name = "data-encoding" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" - -[[package]] -name = "delegate" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "780eb241654bf097afb00fc5f054a09b687dad862e485fdcf8399bb056565370" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "der" version = "0.7.10" @@ -624,7 +395,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ "const-oid", - "pem-rfc7468", "zeroize", ] @@ -650,10 +420,20 @@ dependencies = [ ] [[package]] -name = "dunce" -version = "1.0.5" +name = "dsa" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" +checksum = "48bc224a9084ad760195584ce5abb3c2c34a225fa312a128ad245a6b412b7689" +dependencies = [ + "digest", + "num-bigint-dig", + "num-traits", + "pkcs8", + "rfc6979", + "sha2", + "signature", + "zeroize", +] [[package]] name = "ecdsa" @@ -675,7 +455,6 @@ version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ - "pkcs8", "signature", ] @@ -687,11 +466,8 @@ checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" dependencies = [ "curve25519-dalek", "ed25519", - "rand_core 0.6.4", - "serde", "sha2", "subtle", - "zeroize", ] [[package]] @@ -710,10 +486,8 @@ dependencies = [ "crypto-bigint", "digest", "ff", - "generic-array 0.14.7", + "generic-array", "group", - "hkdf", - "pem-rfc7468", "pkcs8", "rand_core 0.6.4", "sec1", @@ -721,18 +495,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "enum_dispatch" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" -dependencies = [ - "once_cell", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "env_filter" version = "0.1.4" @@ -789,7 +551,7 @@ dependencies = [ "bitvec", "cfg-if", "libc", - "nix 0.23.2", + "nix", "thiserror 1.0.69", ] @@ -825,24 +587,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "flurry" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf5efcf77a4da27927d3ab0509dec5b0954bb3bc59da5a1de9e52642ebd4cdf9" -dependencies = [ - "ahash", - "num_cpus", - "parking_lot", - "seize", -] - -[[package]] -name = "fs_extra" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" - [[package]] name = "funty" version = "2.0.0" @@ -958,17 +702,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "generic-array" -version = "1.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf57c49a95fd1fe24b90b3033bee6dc7e8f1288d51494cb44e627c295e38542" -dependencies = [ - "generic-array 0.14.7", - "rustversion", - "typenum", -] - [[package]] name = "getrandom" version = "0.2.17" @@ -976,10 +709,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi", - "wasm-bindgen", ] [[package]] @@ -996,16 +727,6 @@ dependencies = [ "wasm-bindgen", ] -[[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 = "gio-sys" version = "0.21.5" @@ -1105,7 +826,7 @@ dependencies = [ "num-integer", "num-rational", "option-operations", - "pastey 0.2.1", + "pastey", "pin-project-lite", "smallvec", "thiserror 2.0.17", @@ -1215,76 +936,18 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" -[[package]] -name = "hax-lib" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d9ba66d1739c68e0219b2b2238b5c4145f491ebf181b9c6ab561a19352ae86" -dependencies = [ - "hax-lib-macros", - "num-bigint", - "num-traits", -] - -[[package]] -name = "hax-lib-macros" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24ba777a231a58d1bce1d68313fa6b6afcc7966adef23d60f45b8a2b9b688bf1" -dependencies = [ - "hax-lib-macros-types", - "proc-macro-error2", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "hax-lib-macros-types" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "867e19177d7425140b417cd27c2e05320e727ee682e98368f88b7194e80ad515" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "serde_json", - "uuid", -] - [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" - [[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -[[package]] -name = "hex-literal" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" - -[[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" @@ -1294,15 +957,6 @@ dependencies = [ "digest", ] -[[package]] -name = "home" -version = "0.5.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" -dependencies = [ - "windows-sys 0.61.2", -] - [[package]] name = "iana-time-zone" version = "0.1.64" @@ -1343,36 +997,7 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ - "block-padding", - "generic-array 0.14.7", -] - -[[package]] -name = "internal-russh-forked-ssh-key" -version = "0.6.11+upstream-0.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a77eae781ed6a7709fb15b64862fcca13d886b07c7e2786f5ed34e5e2b9187" -dependencies = [ - "argon2", - "bcrypt-pbkdf", - "ecdsa", - "ed25519-dalek", - "hex", - "hmac", - "num-bigint-dig", - "p256", - "p384", - "p521", - "rand_core 0.6.4", - "rsa", - "sec1", - "sha1", - "sha2", - "signature", - "ssh-cipher", - "ssh-encoding", - "subtle", - "zeroize", + "generic-array", ] [[package]] @@ -1396,16 +1021,6 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" -[[package]] -name = "jobserver" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" -dependencies = [ - "getrandom 0.3.4", - "libc", -] - [[package]] name = "js-sys" version = "0.3.85" @@ -1440,72 +1055,6 @@ version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" -[[package]] -name = "libcrux-intrinsics" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc9ee7ef66569dd7516454fe26de4e401c0c62073929803486b96744594b9632" -dependencies = [ - "core-models", - "hax-lib", -] - -[[package]] -name = "libcrux-ml-kem" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb6a88086bf11bd2ec90926c749c4a427f2e59841437dbdede8cde8a96334ab" -dependencies = [ - "hax-lib", - "libcrux-intrinsics", - "libcrux-platform", - "libcrux-secrets", - "libcrux-sha3", - "libcrux-traits", - "rand 0.9.2", - "tls_codec", -] - -[[package]] -name = "libcrux-platform" -version = "0.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db82d058aa76ea315a3b2092f69dfbd67ddb0e462038a206e1dcd73f058c0778" -dependencies = [ - "libc", -] - -[[package]] -name = "libcrux-secrets" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4dbbf6bc9f2bc0f20dc3bea3e5c99adff3bdccf6d2a40488963da69e2ec307" -dependencies = [ - "hax-lib", -] - -[[package]] -name = "libcrux-sha3" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2400bec764d1c75b8a496d5747cffe32f1fb864a12577f0aca2f55a92021c962" -dependencies = [ - "hax-lib", - "libcrux-intrinsics", - "libcrux-platform", - "libcrux-traits", -] - -[[package]] -name = "libcrux-traits" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9adfd58e79d860f6b9e40e35127bfae9e5bd3ade33201d1347459011a2add034" -dependencies = [ - "libcrux-secrets", - "rand 0.9.2", -] - [[package]] name = "libm" version = "0.2.15" @@ -1532,15 +1081,6 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - [[package]] name = "log" version = "0.4.29" @@ -1565,12 +1105,6 @@ dependencies = [ "regex-automata", ] -[[package]] -name = "md5" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" - [[package]] name = "memchr" version = "2.7.6" @@ -1626,18 +1160,6 @@ dependencies = [ "memoffset", ] -[[package]] -name = "nix" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" -dependencies = [ - "bitflags 2.10.0", - "cfg-if", - "cfg_aliases", - "libc", -] - [[package]] name = "nu-ansi-term" version = "0.50.3" @@ -1647,17 +1169,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "num-bigint" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "num-integer", - "num-traits", - "rand 0.8.5", -] - [[package]] name = "num-bigint-dig" version = "0.8.6" @@ -1720,16 +1231,6 @@ dependencies = [ "libm", ] -[[package]] -name = "num_cpus" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "once_cell" version = "1.21.3" @@ -1742,19 +1243,13 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" -[[package]] -name = "opaque-debug" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" - [[package]] name = "option-operations" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aca39cf52b03268400c16eeb9b56382ea3c3353409309b63f5c8f0b1faf42754" dependencies = [ - "pastey 0.2.1", + "pastey", ] [[package]] @@ -1795,81 +1290,12 @@ dependencies = [ "sha2", ] -[[package]] -name = "pageant" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b537f975f6d8dcf48db368d7ec209d583b015713b5df0f5d92d2631e4ff5595" -dependencies = [ - "byteorder", - "bytes", - "delegate", - "futures", - "log", - "rand 0.8.5", - "sha2", - "thiserror 1.0.69", - "tokio", - "windows", - "windows-strings", -] - -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-link", -] - -[[package]] -name = "password-hash" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" -dependencies = [ - "base64ct", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "pastey" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" - [[package]] name = "pastey" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b867cad97c0791bbd3aaa6472142568c6c9e8f71937e98379f584cfb0cf35bec" -[[package]] -name = "pbkdf2" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" -dependencies = [ - "digest", - "hmac", -] - [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -1902,61 +1328,21 @@ dependencies = [ "spki", ] -[[package]] -name = "pkcs5" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e847e2c91a18bfa887dd028ec33f2fe6f25db77db3619024764914affe8b69a6" -dependencies = [ - "aes", - "cbc", - "der", - "pbkdf2", - "scrypt", - "sha2", - "spki", -] - [[package]] name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "pkcs5", - "rand_core 0.6.4", - "spki", -] - -[[package]] -name = "pkg-config" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" - -[[package]] -name = "poly1305" -version = "0.8.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "cpufeatures", - "opaque-debug", - "universal-hash", + "der", + "spki", ] [[package]] -name = "polyval" -version = "0.6.2" +name = "pkg-config" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" -dependencies = [ - "cfg-if", - "cpufeatures", - "opaque-debug", - "universal-hash", -] +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "powerfmt" @@ -1991,28 +1377,6 @@ dependencies = [ "toml_edit", ] -[[package]] -name = "proc-macro-error-attr2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "proc-macro-error2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" -dependencies = [ - "proc-macro-error-attr2", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "proc-macro2" version = "1.0.105" @@ -2073,7 +1437,6 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ - "libc", "rand_chacha 0.3.1", "rand_core 0.6.4", ] @@ -2126,15 +1489,6 @@ dependencies = [ "getrandom 0.3.4", ] -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags 2.10.0", -] - [[package]] name = "regex-automata" version = "0.4.13" @@ -2157,7 +1511,6 @@ name = "review-client" version = "0.1.0" dependencies = [ "anyhow", - "async-ssh2-tokio", "bson", "clap", "futures", @@ -2169,6 +1522,7 @@ dependencies = [ "review-server", "serde", "serde_json", + "ssh-key", "test-log", "tokio", "tokio-util", @@ -2190,6 +1544,7 @@ dependencies = [ "procfs", "serde", "serde_json", + "ssh-key", "test-log", "tokio", "tokio-util", @@ -2228,110 +1583,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "russh" -version = "0.55.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b4d036bb45d7bbe99dbfef4ec60eaeb614708d22ff107124272f8ef6b54548" -dependencies = [ - "aes", - "aws-lc-rs", - "bitflags 2.10.0", - "block-padding", - "byteorder", - "bytes", - "cbc", - "ctr", - "curve25519-dalek", - "data-encoding", - "delegate", - "der", - "digest", - "ecdsa", - "ed25519-dalek", - "elliptic-curve", - "enum_dispatch", - "flate2", - "futures", - "generic-array 1.3.5", - "getrandom 0.2.17", - "hex-literal", - "hmac", - "home", - "inout", - "internal-russh-forked-ssh-key", - "libcrux-ml-kem", - "log", - "md5", - "num-bigint", - "p256", - "p384", - "p521", - "pageant", - "pbkdf2", - "pkcs1", - "pkcs5", - "pkcs8", - "rand 0.8.5", - "rand_core 0.6.4", - "rsa", - "russh-cryptovec", - "russh-util", - "sec1", - "sha1", - "sha2", - "signature", - "spki", - "ssh-encoding", - "subtle", - "thiserror 1.0.69", - "tokio", - "typenum", - "zeroize", -] - -[[package]] -name = "russh-cryptovec" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb0ed583ff0f6b4aa44c7867dd7108df01b30571ee9423e250b4cc939f8c6cf" -dependencies = [ - "libc", - "log", - "nix 0.29.0", - "ssh-encoding", - "winapi", -] - -[[package]] -name = "russh-sftp" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bb94393cafad0530145b8f626d8687f1ee1dedb93d7ba7740d6ae81868b13b5" -dependencies = [ - "bitflags 2.10.0", - "bytes", - "chrono", - "flurry", - "log", - "serde", - "thiserror 2.0.17", - "tokio", - "tokio-util", -] - -[[package]] -name = "russh-util" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668424a5dde0bcb45b55ba7de8476b93831b4aa2fa6947e145f3b053e22c60b6" -dependencies = [ - "chrono", - "tokio", - "wasm-bindgen", - "wasm-bindgen-futures", -] - [[package]] name = "rustc_version" version = "0.4.1" @@ -2360,32 +1611,6 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" -[[package]] -name = "salsa20" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" -dependencies = [ - "cipher", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "scrypt" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" -dependencies = [ - "pbkdf2", - "salsa20", - "sha2", -] - [[package]] name = "sec1" version = "0.7.3" @@ -2394,18 +1619,12 @@ checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct", "der", - "generic-array 0.14.7", + "generic-array", "pkcs8", "subtle", "zeroize", ] -[[package]] -name = "seize" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "689224d06523904ebcc9b482c6a3f4f7fb396096645c4cd10c0d2ff7371a34d3" - [[package]] name = "semver" version = "1.0.27" @@ -2577,15 +1796,8 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "caac132742f0d33c3af65bfcde7f6aa8f62f0e991d80db99149eb9d44708784f" dependencies = [ - "aes", - "aes-gcm", - "cbc", - "chacha20", "cipher", - "ctr", - "poly1305", "ssh-encoding", - "subtle", ] [[package]] @@ -2595,11 +1807,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb9242b9ef4108a78e8cd1a2c98e193ef372437f8c22be363075233321dd4a15" dependencies = [ "base64ct", - "bytes", "pem-rfc7468", "sha2", ] +[[package]] +name = "ssh-key" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b86f5297f0f04d08cabaa0f6bff7cb6aec4d9c3b49d87990d63da9d9156a8c3" +dependencies = [ + "dsa", + "ed25519-dalek", + "num-bigint-dig", + "p256", + "p384", + "p521", + "rand_core 0.6.4", + "rsa", + "sec1", + "serde", + "sha1", + "sha2", + "signature", + "ssh-cipher", + "ssh-encoding", + "subtle", + "zeroize", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -2756,36 +1992,6 @@ dependencies = [ "time-core", ] -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - -[[package]] -name = "tls_codec" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de2e01245e2bb89d6f05801c564fa27624dbd7b1846859876c7dad82e90bf6b" -dependencies = [ - "tls_codec_derive", - "zeroize", -] - -[[package]] -name = "tls_codec_derive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "tokio" version = "1.49.0" @@ -2955,22 +2161,6 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" -[[package]] -name = "universal-hash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" -dependencies = [ - "crypto-common", - "subtle", -] - -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - [[package]] name = "utf8parse" version = "0.2.2" @@ -3035,20 +2225,6 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" -dependencies = [ - "cfg-if", - "futures-util", - "js-sys", - "once_cell", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "wasm-bindgen-macro" version = "0.2.108" @@ -3081,59 +2257,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "web-sys" -version = "0.3.85" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" -dependencies = [ - "windows-collections", - "windows-core", - "windows-future", - "windows-numerics", -] - -[[package]] -name = "windows-collections" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" -dependencies = [ - "windows-core", -] - [[package]] name = "windows-core" version = "0.62.2" @@ -3147,17 +2270,6 @@ dependencies = [ "windows-strings", ] -[[package]] -name = "windows-future" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" -dependencies = [ - "windows-core", - "windows-link", - "windows-threading", -] - [[package]] name = "windows-implement" version = "0.60.2" @@ -3186,16 +2298,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" -[[package]] -name = "windows-numerics" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" -dependencies = [ - "windows-core", - "windows-link", -] - [[package]] name = "windows-result" version = "0.4.1" @@ -3249,15 +2351,6 @@ dependencies = [ "windows_x86_64_msvc", ] -[[package]] -name = "windows-threading" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" -dependencies = [ - "windows-link", -] - [[package]] name = "windows_aarch64_gnullvm" version = "0.53.1" @@ -3355,20 +2448,6 @@ name = "zeroize" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] [[package]] name = "zmij" diff --git a/client/Cargo.toml b/client/Cargo.toml index 10d0a10..5a18811 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -7,7 +7,6 @@ edition = "2024" [dependencies] tokio = { version = "1.49.0", features = ["macros", "net", "rt-multi-thread"] } -async-ssh2-tokio = "0.12.1" clap = { version = "4.5", features = ["derive"] } serde = { version = "1.0.228", features = ["derive"] } serde_json = "1.0.149" @@ -24,3 +23,4 @@ tokio-util = { version = "0.7.18", features = ["io", "io-util", "codec"] } review-server = { path = "../server" } futures = "0.3.31" bson = { version = "3.1.0", features = ["serde"] } +ssh-key = { version = "0.6.7", features = ["p256", "p384", "p521", "dsa", "ed25519", "rsa", "serde"] } diff --git a/client/src/config.rs b/client/src/config.rs index 9365a5f..3ac676e 100644 --- a/client/src/config.rs +++ b/client/src/config.rs @@ -1,4 +1,7 @@ -use std::env::{self, VarError}; +use std::{ + env::{self, VarError}, + path::PathBuf, +}; use anyhow::{Context, Error}; use clap::Parser; @@ -20,11 +23,10 @@ pub struct CliOptions { #[arg(long, name = "tcp-port")] tcp_port: Option, - /* /// Private SSH key file path #[arg(long, name = "ssh-key")] ssh_key: Option, - */ + /// Framerate (default: 50) #[arg(long, name = "framerate")] framerate: Option, @@ -41,7 +43,7 @@ pub struct CliOptions { #[derive(Debug, Clone)] pub struct ClientOptions { pub remarkable_ip: String, - //pub ssh_key: PathBuf, + pub ssh_key: PathBuf, pub dark_mode: bool, pub tcp_port: u16, pub show_cursor: bool, @@ -56,7 +58,6 @@ impl From for ClientOptions { "REMARKABLE_IP", DEFAULT_IP.to_string(), ), - //ssh_key: must_resolve_option(value.ssh_key, "REMARKABLE_SSH_KEY_PATH"), tcp_port: resolve_with( value.tcp_port, "REMARKABLE_TCP_PORT", @@ -67,6 +68,7 @@ impl From for ClientOptions { }, DEFAULT_TCP_PORT, ), + ssh_key: must_resolve_option(value.ssh_key, "REMARKABLE_SSH_KEY_PATH"), framerate: resolve_with( value.framerate, "REMARKABLE_FRAMERATE", diff --git a/client/src/connection/mod.rs b/client/src/connection/mod.rs index a30afb2..7a1b48b 100644 --- a/client/src/connection/mod.rs +++ b/client/src/connection/mod.rs @@ -1,11 +1,15 @@ +mod ssh; pub mod video; +use std::any::type_name; + use anyhow::{Context, Error}; use futures::{SinkExt, StreamExt}; use review_server::{ config::{StreamConfig, device::DeviceConfig}, version::VersionInfo, }; +use serde::{Serialize, de::DeserializeOwned}; use tokio::net::TcpStream; use tokio_util::codec::{Framed, LengthDelimitedCodec}; use tracing::info; @@ -22,38 +26,78 @@ impl Connection { info!("setting up TCP connection"); let stream = TcpStream::connect(format!( "{}:{}", - client_options.remarkable_ip, client_options.tcp_port + client_options.remarkable_ip, client_options.tcp_port, )) .await .context("could not connect to TCP stream")?; + stream.set_nodelay(true).context("could not set nodelay")?; + let framed = Framed::new(stream, LengthDelimitedCodec::new()); Ok(Connection { framed }) } - pub async fn receive_version_info(&mut self) -> Result { + pub async fn exchange_information( + &mut self, + client_options: ClientOptions, + ) -> Result { + self.authenticate(client_options.clone()) + .await + .context("error while authenticating")?; + + let version_info: VersionInfo = self + .receive() + .await + .context("could not receive version info")?; + + info!("received version information: {}", version_info); + + let device_config = DeviceConfig::new(version_info).context(format!( + "could not get device configuration for version {}", + version_info, + ))?; + + let stream_config = StreamConfig { + device_config: device_config.clone(), + framerate: client_options.framerate, + show_cursor: client_options.show_cursor, + }; + + info!("sending out stream config {:?}", &stream_config); + + self.send(&stream_config) + .await + .context("could not send device config")?; + + Ok(device_config) + } + + async fn receive(&mut self) -> Result { let msg = self .framed .next() .await - .context("connection was dropped before version info was communicated")? - .context("could not receive version info message")?; + .context("connection closed before stream configuration was sent")? + .context("could not message with stream configuration")? + .clone(); - let version_info = - bson::deserialize_from_slice(&msg).context("could not deserialize version info")?; + let stream_config = bson::deserialize_from_slice(&msg).context(format!( + "could not deserialize message of type {}", + type_name::(), + ))?; - Ok(version_info) + Ok(stream_config) } - pub async fn send_stream_config(&mut self, stream_config: StreamConfig) -> Result<(), Error> { - let msg = - bson::serialize_to_vec(&stream_config).context("could not serialize stream config")?; + pub async fn send(&mut self, value: &T) -> Result<(), Error> { + let msg = bson::serialize_to_vec(value) + .context(format!("could not serialize type {}", type_name::()))?; self.framed .send(msg.into()) .await - .context("could not send stream config") + .context("could not send serialized version information") .map(|_| ()) } } diff --git a/client/src/connection/ssh/mod.rs b/client/src/connection/ssh/mod.rs new file mode 100644 index 0000000..12f0286 --- /dev/null +++ b/client/src/connection/ssh/mod.rs @@ -0,0 +1,32 @@ +use std::{fs::read_to_string, path::PathBuf}; + +use anyhow::{Context, Error}; +use futures::SinkExt; +use ssh_key::{PrivateKey, PublicKey}; +use tracing::debug; + +use crate::config::ClientOptions; + +use super::Connection; + +impl Connection { + pub async fn authenticate(&mut self, client_options: ClientOptions) -> Result<(), Error> { + let key = get_ssh_key(&client_options.ssh_key).context("could not get private SSH key")?; + let pub_key = key.public_key(); + + self.send(&pub_key) + .await + .context("could not send public SSH key")?; + + Ok(()) + } +} + +fn get_ssh_key(key_file_path: &PathBuf) -> Result { + let content = read_to_string(key_file_path).context("could not read private SSH key file")?; + let key = PrivateKey::from_openssh(content)?; + + debug!("read private key: {:?}", key); + + Ok(key) +} diff --git a/client/src/main.rs b/client/src/main.rs index 3a646d2..0cf21d9 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -6,7 +6,6 @@ use anyhow::{Context, Error}; use clap::Parser; use config::{CliOptions, ClientOptions}; use connection::{Connection, video::VideoConnection}; -use review_server::config::{StreamConfig, device::DeviceConfig}; use tracing::{debug, info}; #[tokio::main] @@ -27,29 +26,10 @@ async fn main() -> Result<(), Error> { .await .context("could not initialize TCP connection")?; - let version_info = conn - .receive_version_info() + let device_config = conn + .exchange_information(client_options.clone()) .await - .context("could not receive version info")?; - - info!("received version information: {}", version_info); - - let device_config = DeviceConfig::new(version_info).context(format!( - "could not get device configuration for version {}", - version_info, - ))?; - - let stream_config = StreamConfig { - device_config: device_config.clone(), - framerate: client_options.framerate, - show_cursor: client_options.show_cursor, - }; - - info!("sending out stream config {:?}", &stream_config); - - conn.send_stream_config(stream_config) - .await - .context("could not send device config")?; + .context("error during initial information exchange")?; let mut video_connection = VideoConnection::new(conn, device_config.video_config) .context("could not initialize video connection")?; diff --git a/server/Cargo.toml b/server/Cargo.toml index f8f711b..1c17c50 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -21,3 +21,4 @@ futures = "0.3.31" bson = { version = "3.1.0", features = ["serde"] } procfs = "0.18.0" itertools = "0.14.0" +ssh-key = { version = "0.6.7", features = ["p256", "p384", "p521", "dsa", "ed25519", "rsa", "serde"] } diff --git a/server/src/connection/mod.rs b/server/src/connection/mod.rs index f5504a5..bbe1546 100644 --- a/server/src/connection/mod.rs +++ b/server/src/connection/mod.rs @@ -1,9 +1,15 @@ +pub mod ssh; pub mod video; +use std::any::type_name; + use anyhow::{Context, Error}; use futures::{StreamExt, sink::SinkExt}; +use serde::de::DeserializeOwned; +use serde::{Deserialize, Serialize}; use tokio::net::TcpStream; use tokio_util::codec::{Framed, LengthDelimitedCodec}; +use tracing::info; use crate::config::StreamConfig; use crate::version::VersionInfo; @@ -20,28 +26,60 @@ impl Connection { Connection { framed } } - pub async fn send_version_info(&mut self, version_info: VersionInfo) -> Result<(), Error> { - let msg = bson::serialize_to_vec(&version_info) - .context("could not serialize version information")?; + pub async fn exchange_information(&mut self) -> Result { + self.authenticate() + .await + .context("error while authenticating client")?; - self.framed - .send(msg.into()) + let version_info = + VersionInfo::get_from_device().context("could not get version information")?; + + info!( + "got version information: hardware {:?}, firmware {}", + version_info.hardware, version_info.firmware, + ); + + info!("sending out version information"); + + self.send(&version_info) .await - .context("could not send serialized version information") - .map(|_| ()) + .context("could not send out version information")?; + + let stream_config: StreamConfig = self + .receive() + .await + .context("could not receive stream config")?; + + info!("received stream config {:?}", &stream_config); + + Ok(stream_config) } - pub async fn receive_stream_config(&mut self) -> Result { + async fn receive(&mut self) -> Result { let msg = self .framed .next() .await .context("connection closed before stream configuration was sent")? - .context("could not message with stream configuration")?; + .context("could not message with stream configuration")? + .clone(); - let stream_config = - bson::deserialize_from_slice(&msg).context("could not deserialize stream config")?; + let stream_config = bson::deserialize_from_slice(&msg).context(format!( + "could not deserialize message of type {}", + type_name::(), + ))?; Ok(stream_config) } + + pub async fn send(&mut self, value: &T) -> Result<(), Error> { + let msg = bson::serialize_to_vec(value) + .context(format!("could not serialize type {}", type_name::()))?; + + self.framed + .send(msg.into()) + .await + .context("could not send serialized version information") + .map(|_| ()) + } } diff --git a/server/src/connection/ssh/mod.rs b/server/src/connection/ssh/mod.rs new file mode 100644 index 0000000..f24f8f7 --- /dev/null +++ b/server/src/connection/ssh/mod.rs @@ -0,0 +1,18 @@ +use anyhow::{Context, Error}; +use ssh_key::PublicKey; +use tracing::debug; + +use super::Connection; + +impl Connection { + pub async fn authenticate(&mut self) -> Result<(), Error> { + let pub_key: PublicKey = self + .receive() + .await + .context("could not receive public SSH key")?; + + debug!("received public SSH key: {:?}", pub_key); + + Ok(()) + } +} diff --git a/server/src/main.rs b/server/src/main.rs index 7dae366..0a9ea40 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -54,38 +54,24 @@ impl Server { .await .context("error while waiting for a TCP connection")?; + stream.set_nodelay(true).context("could not set nodelay")?; + info!("new connection from {}", addr); let conn = Connection::new(stream); spawn(async move { if let Err(error) = Self::task(conn).await { - error!("connection terminated with error {}", error); + error!("connection terminated with error {:?}", error); } }); } } async fn task(mut conn: Connection) -> Result<(), Error> { - let version_info = - VersionInfo::get_from_device().context("could not get version information")?; - - info!( - "got version information: hardware {:?}, firmware {}", - version_info.hardware, version_info.firmware, - ); - - info!("sending out version information"); - - conn.send_version_info(version_info) - .await - .context("could not send out version information")?; - let stream_config = conn - .receive_stream_config() + .exchange_information() .await - .context("could not receive stream config")?; - - info!("received stream config {:?}", &stream_config); + .context("error during initial information exchange")?; let mut video_conn = VideoConnection::new(conn, stream_config) .context("could not initialize video connection")?; From df64c9868bd6cfc9c2b0fc11b01f6d866e296cae Mon Sep 17 00:00:00 2001 From: Fabian Weik Date: Sat, 24 Jan 2026 21:10:33 +0100 Subject: [PATCH 03/26] switch to serde_json --- Cargo.lock | 213 ++----------------------------- client/Cargo.toml | 1 - client/src/connection/mod.rs | 4 +- client/src/connection/ssh/mod.rs | 5 +- server/Cargo.toml | 1 - server/src/connection/mod.rs | 4 +- 6 files changed, 19 insertions(+), 209 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 07aeca5..10f110b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,19 +8,6 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" -[[package]] -name = "ahash" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" -dependencies = [ - "cfg-if", - "getrandom 0.3.4", - "once_cell", - "version_check", - "zerocopy", -] - [[package]] name = "aho-corasick" version = "1.1.4" @@ -122,12 +109,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - [[package]] name = "base64ct" version = "1.8.3" @@ -167,28 +148,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "bson" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3f109694c4f45353972af96bf97d8a057f82e2d6e496457f4d135b9867a518c" -dependencies = [ - "ahash", - "base64", - "bitvec", - "getrandom 0.3.4", - "hex", - "indexmap", - "js-sys", - "rand 0.9.2", - "serde", - "serde_bytes", - "simdutf8", - "thiserror 2.0.17", - "time", - "uuid", -] - [[package]] name = "bumpalo" version = "3.19.1" @@ -347,7 +306,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", - "rand_core 0.6.4", + "rand_core", "subtle", "zeroize", ] @@ -398,15 +357,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "deranged" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" -dependencies = [ - "powerfmt", -] - [[package]] name = "digest" version = "0.10.7" @@ -489,7 +439,7 @@ dependencies = [ "generic-array", "group", "pkcs8", - "rand_core 0.6.4", + "rand_core", "sec1", "subtle", "zeroize", @@ -561,7 +511,7 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" dependencies = [ - "rand_core 0.6.4", + "rand_core", "subtle", ] @@ -713,20 +663,6 @@ dependencies = [ "wasi", ] -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "r-efi", - "wasip2", - "wasm-bindgen", -] - [[package]] name = "gio-sys" version = "0.21.5" @@ -802,7 +738,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ "ff", - "rand_core 0.6.4", + "rand_core", "subtle", ] @@ -1180,17 +1116,11 @@ dependencies = [ "num-integer", "num-iter", "num-traits", - "rand 0.8.5", + "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" @@ -1286,7 +1216,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "primeorder", - "rand_core 0.6.4", + "rand_core", "sha2", ] @@ -1344,12 +1274,6 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - [[package]] name = "ppv-lite86" version = "0.2.21" @@ -1419,12 +1343,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - [[package]] name = "radium" version = "0.7.0" @@ -1437,18 +1355,8 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.5", + "rand_chacha", + "rand_core", ] [[package]] @@ -1458,17 +1366,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.5", + "rand_core", ] [[package]] @@ -1477,16 +1375,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.17", -] - -[[package]] -name = "rand_core" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" -dependencies = [ - "getrandom 0.3.4", + "getrandom", ] [[package]] @@ -1511,7 +1400,6 @@ name = "review-client" version = "0.1.0" dependencies = [ "anyhow", - "bson", "clap", "futures", "gstreamer", @@ -1535,7 +1423,6 @@ name = "review-server" version = "0.1.0" dependencies = [ "anyhow", - "bson", "clap", "futures", "itertools", @@ -1575,7 +1462,7 @@ dependencies = [ "num-traits", "pkcs1", "pkcs8", - "rand_core 0.6.4", + "rand_core", "sha2", "signature", "spki", @@ -1641,16 +1528,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde_bytes" -version = "0.11.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" -dependencies = [ - "serde", - "serde_core", -] - [[package]] name = "serde_core" version = "1.0.228" @@ -1737,7 +1614,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest", - "rand_core 0.6.4", + "rand_core", ] [[package]] @@ -1746,12 +1623,6 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" -[[package]] -name = "simdutf8" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" - [[package]] name = "slab" version = "0.4.11" @@ -1823,7 +1694,7 @@ dependencies = [ "p256", "p384", "p521", - "rand_core 0.6.4", + "rand_core", "rsa", "sec1", "serde", @@ -1961,37 +1832,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "time" -version = "0.3.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9e442fc33d7fdb45aa9bfeb312c095964abdf596f7567261062b2a7107aaabd" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde_core", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b36ee98fd31ec7426d599183e8fe26932a8dc1fb76ddb6214d05493377d34ca" - -[[package]] -name = "time-macros" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e552d1249bf61ac2a52db88179fd0673def1e1ad8243a00d9ec9ed71fee3dd" -dependencies = [ - "num-conv", - "time-core", -] - [[package]] name = "tokio" version = "1.49.0" @@ -2167,18 +2007,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" -[[package]] -name = "uuid" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" -dependencies = [ - "getrandom 0.3.4", - "js-sys", - "serde_core", - "wasm-bindgen", -] - [[package]] name = "valuable" version = "0.1.1" @@ -2203,15 +2031,6 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" -[[package]] -name = "wasip2" -version = "1.0.2+wasi-0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" -dependencies = [ - "wit-bindgen", -] - [[package]] name = "wasm-bindgen" version = "0.2.108" @@ -2408,12 +2227,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" - [[package]] name = "wyz" version = "0.5.1" diff --git a/client/Cargo.toml b/client/Cargo.toml index 5a18811..b680d9b 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -22,5 +22,4 @@ lz4_flex = "0.12.0" tokio-util = { version = "0.7.18", features = ["io", "io-util", "codec"] } review-server = { path = "../server" } futures = "0.3.31" -bson = { version = "3.1.0", features = ["serde"] } ssh-key = { version = "0.6.7", features = ["p256", "p384", "p521", "dsa", "ed25519", "rsa", "serde"] } diff --git a/client/src/connection/mod.rs b/client/src/connection/mod.rs index 7a1b48b..cc62f57 100644 --- a/client/src/connection/mod.rs +++ b/client/src/connection/mod.rs @@ -82,7 +82,7 @@ impl Connection { .context("could not message with stream configuration")? .clone(); - let stream_config = bson::deserialize_from_slice(&msg).context(format!( + let stream_config = serde_json::from_slice(&msg).context(format!( "could not deserialize message of type {}", type_name::(), ))?; @@ -91,7 +91,7 @@ impl Connection { } pub async fn send(&mut self, value: &T) -> Result<(), Error> { - let msg = bson::serialize_to_vec(value) + let msg = serde_json::to_vec(value) .context(format!("could not serialize type {}", type_name::()))?; self.framed diff --git a/client/src/connection/ssh/mod.rs b/client/src/connection/ssh/mod.rs index 12f0286..ec74049 100644 --- a/client/src/connection/ssh/mod.rs +++ b/client/src/connection/ssh/mod.rs @@ -1,8 +1,7 @@ use std::{fs::read_to_string, path::PathBuf}; use anyhow::{Context, Error}; -use futures::SinkExt; -use ssh_key::{PrivateKey, PublicKey}; +use ssh_key::PrivateKey; use tracing::debug; use crate::config::ClientOptions; @@ -14,7 +13,7 @@ impl Connection { let key = get_ssh_key(&client_options.ssh_key).context("could not get private SSH key")?; let pub_key = key.public_key(); - self.send(&pub_key) + self.send(pub_key) .await .context("could not send public SSH key")?; diff --git a/server/Cargo.toml b/server/Cargo.toml index 1c17c50..ba9a15f 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -18,7 +18,6 @@ test-log = { version = "0.2.19", features = ["trace"] } lz4_flex = { version = "0.12.0", features = ["frame"] } tokio-util = { version = "0.7.18", features = ["codec"] } futures = "0.3.31" -bson = { version = "3.1.0", features = ["serde"] } procfs = "0.18.0" itertools = "0.14.0" ssh-key = { version = "0.6.7", features = ["p256", "p384", "p521", "dsa", "ed25519", "rsa", "serde"] } diff --git a/server/src/connection/mod.rs b/server/src/connection/mod.rs index bbe1546..b9a95d4 100644 --- a/server/src/connection/mod.rs +++ b/server/src/connection/mod.rs @@ -64,7 +64,7 @@ impl Connection { .context("could not message with stream configuration")? .clone(); - let stream_config = bson::deserialize_from_slice(&msg).context(format!( + let stream_config = serde_json::from_slice(&msg).context(format!( "could not deserialize message of type {}", type_name::(), ))?; @@ -73,7 +73,7 @@ impl Connection { } pub async fn send(&mut self, value: &T) -> Result<(), Error> { - let msg = bson::serialize_to_vec(value) + let msg = serde_json::to_vec(value) .context(format!("could not serialize type {}", type_name::()))?; self.framed From 1f2ee4907be6e74803e2438f95e4225849258689 Mon Sep 17 00:00:00 2001 From: Fabian Weik Date: Sat, 24 Jan 2026 21:37:00 +0100 Subject: [PATCH 04/26] check whether public key is authorized --- server/Cargo.toml | 2 +- server/src/connection/ssh/mod.rs | 24 ++++++++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/server/Cargo.toml b/server/Cargo.toml index ba9a15f..8414dc6 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -20,4 +20,4 @@ tokio-util = { version = "0.7.18", features = ["codec"] } futures = "0.3.31" procfs = "0.18.0" itertools = "0.14.0" -ssh-key = { version = "0.6.7", features = ["p256", "p384", "p521", "dsa", "ed25519", "rsa", "serde"] } +ssh-key = { version = "0.6.7", features = ["p256", "p384", "p521", "dsa", "ed25519", "rsa", "serde", "std"] } diff --git a/server/src/connection/ssh/mod.rs b/server/src/connection/ssh/mod.rs index f24f8f7..dde1c10 100644 --- a/server/src/connection/ssh/mod.rs +++ b/server/src/connection/ssh/mod.rs @@ -1,18 +1,38 @@ -use anyhow::{Context, Error}; -use ssh_key::PublicKey; +use std::{path::PathBuf, str::FromStr}; + +use anyhow::{Context, Error, anyhow}; +use ssh_key::{AuthorizedKeys, PublicKey, authorized_keys::Entry}; use tracing::debug; use super::Connection; impl Connection { pub async fn authenticate(&mut self) -> Result<(), Error> { + let authorized_keys = get_authorized_keys().context("could not get authorized keys")?; + let pub_key: PublicKey = self .receive() .await .context("could not receive public SSH key")?; + let pub_key_authorized = authorized_keys + .iter() + .any(|authorized_key| authorized_key.public_key() == &pub_key); + + if !pub_key_authorized { + return Err(anyhow!("public key is not authorized")); + } + debug!("received public SSH key: {:?}", pub_key); Ok(()) } } + +fn get_authorized_keys<'a>() -> Result, Error> { + AuthorizedKeys::read_file( + PathBuf::from_str("/home/root/.ssh/authorized_keys") + .context("could not build path of authorized keys")?, + ) + .context("could not read authorized keys file") +} From 6978fd5d1fd84a87658f485ce61a7f89492dd463 Mon Sep 17 00:00:00 2001 From: Fabian Weik Date: Sat, 24 Jan 2026 21:41:48 +0100 Subject: [PATCH 05/26] fix copied error contexts --- client/src/connection/mod.rs | 16 ++++++++++++---- server/src/connection/mod.rs | 16 ++++++++++++---- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/client/src/connection/mod.rs b/client/src/connection/mod.rs index cc62f57..5508829 100644 --- a/client/src/connection/mod.rs +++ b/client/src/connection/mod.rs @@ -78,9 +78,14 @@ impl Connection { .framed .next() .await - .context("connection closed before stream configuration was sent")? - .context("could not message with stream configuration")? - .clone(); + .context(format!( + "connection closed before message of type {} was sent", + type_name::(), + ))? + .context(format!( + "could not send message of type {}", + type_name::() + ))?; let stream_config = serde_json::from_slice(&msg).context(format!( "could not deserialize message of type {}", @@ -97,7 +102,10 @@ impl Connection { self.framed .send(msg.into()) .await - .context("could not send serialized version information") + .context(format!( + "could not send serialized message of type {}", + type_name::() + )) .map(|_| ()) } } diff --git a/server/src/connection/mod.rs b/server/src/connection/mod.rs index b9a95d4..7696fae 100644 --- a/server/src/connection/mod.rs +++ b/server/src/connection/mod.rs @@ -60,9 +60,14 @@ impl Connection { .framed .next() .await - .context("connection closed before stream configuration was sent")? - .context("could not message with stream configuration")? - .clone(); + .context(format!( + "connection closed before message of type {} was sent", + type_name::(), + ))? + .context(format!( + "could not send message of type {}", + type_name::() + ))?; let stream_config = serde_json::from_slice(&msg).context(format!( "could not deserialize message of type {}", @@ -79,7 +84,10 @@ impl Connection { self.framed .send(msg.into()) .await - .context("could not send serialized version information") + .context(format!( + "could not send serialized message of type {}", + type_name::() + )) .map(|_| ()) } } From ad240b83193dd49df179e277906754e5cc81695b Mon Sep 17 00:00:00 2001 From: Fabian Weik Date: Sat, 24 Jan 2026 21:46:41 +0100 Subject: [PATCH 06/26] update readme title --- README.org | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.org b/README.org index bc616b5..cbb92e7 100644 --- a/README.org +++ b/README.org @@ -1,5 +1,6 @@ -#+title: Readme +#+title: reView +Live stream and take snapshots of the contents of your [[https://remarkable.com/][reMarkable tablet]]. Based on [[https://github.com/rien/reStream][reStream]] Please note that this project is still under heavy development. @@ -10,6 +11,15 @@ If you run this program, anyone on your network could connect to your reMarkable + + + + + + + + + From 48ff5791fc19d130284112551bba9210607fde1f Mon Sep 17 00:00:00 2001 From: Fabian Weik Date: Sat, 24 Jan 2026 21:48:42 +0100 Subject: [PATCH 07/26] split shields --- README.org | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/README.org b/README.org index cbb92e7..a0d99fb 100644 --- a/README.org +++ b/README.org @@ -1,25 +1,30 @@ #+title: reView Live stream and take snapshots of the contents of your [[https://remarkable.com/][reMarkable tablet]]. -Based on [[https://github.com/rien/reStream][reStream]] - -Please note that this project is still under heavy development. -If you run this program, anyone on your network could connect to your reMarkable and stream the contents of your screen. +Based on [[https://github.com/rien/reStream][reStream]]. #+BEGIN_HTML

- - - - + - + - + +

+#+END_HTML + +Please note that this project is still under heavy development. +If you run this program, anyone on your network could connect to your reMarkable and stream the contents of your screen. + +#+BEGIN_HTML +

+ + + From 2c5615c92313e21a48df4bf344c81544dc8d6df3 Mon Sep 17 00:00:00 2001 From: Fabian Weik Date: Sun, 25 Jan 2026 10:50:55 +0100 Subject: [PATCH 08/26] read all private keys in the default SSH directory --- README.org | 5 +-- client/src/config.rs | 50 +++++++++++++++---------- client/src/connection/ssh/mod.rs | 64 ++++++++++++++++++++++++++++++-- 3 files changed, 94 insertions(+), 25 deletions(-) diff --git a/README.org b/README.org index a0d99fb..d315cc8 100644 --- a/README.org +++ b/README.org @@ -79,8 +79,6 @@ TODO: Install it as service Options for the client can be set as command line options or environment variables. Command line options override the environment variables. -Values with no default value in their description have to be set (for example =--ssh-key=). - *** Command Line Options #+begin_src @@ -89,6 +87,7 @@ Usage: review-client [OPTIONS] Options: --remarkable-ip IP of the reMarkable tablet (default: 10.11.99.1) --tcp-port TCP port for video stream (default: 6680) + --ssh-key Private SSH key file path. If not specified, attempting all keys in the SSH directory --framerate Framerate (default: 50) --dark-mode Dark mode - invert colors (default: false) --show-cursor Show cursor (default: false) @@ -101,7 +100,7 @@ Options: Corresponding keys: - =REMARKABLE_IP= - =REMARKABLE_TCP_PORT= -# - =REMARKABLE_SSH_KEY_PATH= +- =REMARKABLE_SSH_KEY_PATH= - =REMARKABLE_FRAMERATE= - =REMARKABLE_DARK_MODE= - =REMARKABLE_SHOW_CURSOR= diff --git a/client/src/config.rs b/client/src/config.rs index 3ac676e..23250e9 100644 --- a/client/src/config.rs +++ b/client/src/config.rs @@ -1,13 +1,14 @@ use std::{ env::{self, VarError}, path::PathBuf, + str::FromStr, }; use anyhow::{Context, Error}; use clap::Parser; +use tracing::trace; const DEFAULT_IP: &str = "10.11.99.1"; -const DEFAULT_SSH_PORT: u16 = 22; const DEFAULT_TCP_PORT: u16 = 6680; const DEFAULT_FRAMERATE: f32 = 50.; @@ -23,7 +24,7 @@ pub struct CliOptions { #[arg(long, name = "tcp-port")] tcp_port: Option, - /// Private SSH key file path + /// Private SSH key file path. If not specified, attempting all keys in the SSH directory #[arg(long, name = "ssh-key")] ssh_key: Option, @@ -43,7 +44,7 @@ pub struct CliOptions { #[derive(Debug, Clone)] pub struct ClientOptions { pub remarkable_ip: String, - pub ssh_key: PathBuf, + pub ssh_key: Option, pub dark_mode: bool, pub tcp_port: u16, pub show_cursor: bool, @@ -68,7 +69,9 @@ impl From for ClientOptions { }, DEFAULT_TCP_PORT, ), - ssh_key: must_resolve_option(value.ssh_key, "REMARKABLE_SSH_KEY_PATH"), + ssh_key: resolve_with_optional(value.ssh_key, "REMARKABLE_SSH_KEY_PATH", |string| { + PathBuf::from_str(&string).context("could not parse path of private SSH key") + }), framerate: resolve_with( value.framerate, "REMARKABLE_FRAMERATE", @@ -94,10 +97,6 @@ fn resolve_option>(cli_value: Option, variable_name: &str, de ) } -fn must_resolve_option>(cli_value: Option, variable_name: &str) -> T { - must_resolve_with(cli_value, variable_name, |env_value| Ok(env_value.into())) -} - fn resolve_boolean_option(cli_value: bool, variable_name: &str, default: bool) -> bool { let cli_value = if cli_value { Some(true) } else { None }; resolve_with( @@ -133,6 +132,10 @@ fn resolve_with( return default; } + trace!( + "read environment variable '{}': {:?}", + variable_name, env_string, + ); parse(env_string.expect(&format!( "could not get environment varialbe '{}'", variable_name, @@ -143,21 +146,30 @@ fn resolve_with( )) } -fn must_resolve_with( +fn resolve_with_optional( cli_value: Option, variable_name: &str, parse: impl FnOnce(String) -> Result, -) -> T { +) -> Option { if let Some(cli_value) = cli_value { - return cli_value; + return Some(cli_value); } - let env_string = env::var(variable_name).expect(&format!( - "could not get environment varialbe '{}'", - variable_name, - )); - parse(env_string).expect(&format!( - "could not parse environemt variable '{}'", - variable_name, - )) + let env_string = env::var(variable_name); + if let Ok(env_string) = env_string { + if env_string.is_empty() { + return None; + } + + trace!( + "read environment variable '{}': {}", + variable_name, env_string, + ); + return Some(parse(env_string).expect(&format!( + "could not parse environemt variable '{}'", + variable_name, + ))); + } + + None } diff --git a/client/src/connection/ssh/mod.rs b/client/src/connection/ssh/mod.rs index ec74049..7b0a4b1 100644 --- a/client/src/connection/ssh/mod.rs +++ b/client/src/connection/ssh/mod.rs @@ -1,8 +1,8 @@ -use std::{fs::read_to_string, path::PathBuf}; +use std::{env::home_dir, fs::read_to_string, path::PathBuf}; -use anyhow::{Context, Error}; +use anyhow::{Context, Error, anyhow}; use ssh_key::PrivateKey; -use tracing::debug; +use tracing::{debug, info, trace}; use crate::config::ClientOptions; @@ -10,17 +10,74 @@ use super::Connection; impl Connection { pub async fn authenticate(&mut self, client_options: ClientOptions) -> Result<(), Error> { + let keys_to_check = get_keys_to_check(&client_options.ssh_key) + .context("could not get private keys to check")?; + + info!("found {} private SSH keys to check", keys_to_check.len()); + + /* let key = get_ssh_key(&client_options.ssh_key).context("could not get private SSH key")?; let pub_key = key.public_key(); self.send(pub_key) .await .context("could not send public SSH key")?; + */ Ok(()) } } +fn get_keys_to_check(private_key_path: &Option) -> Result, Error> { + if let Some(private_key_path) = private_key_path { + debug!( + "private key at {} explicitly defined, only loading this key", + private_key_path.to_string_lossy() + ); + return Ok(vec![ + load_private_key(private_key_path) + .context("could not load explicitly specified private SSH key")?, + ]); + } + debug!("no private key explicitly defined, loading keys from .ssh directory"); + + let ssh_directory = home_dir() + .context("could not get home directory")? + .join(".ssh"); + + if !ssh_directory.exists() || ssh_directory.is_file() { + return Err(anyhow!( + "SSH directory '{}' does not exist or is a file", + ssh_directory.to_string_lossy() + )); + } + + let dir_entries = ssh_directory + .read_dir() + .context("could not read the SSH directory")?; + + Ok(dir_entries + .filter_map(|entry| entry.ok()) + .filter_map(|entry| { + let name = entry.file_name(); + let name = name.to_str()?; + trace!("attemting to parse {} as a private SSH key", name); + let private_key = load_private_key(&ssh_directory.join(name)).ok()?; + trace!("successfully parsed {} as a private SSH key", name); + + Some(private_key) + }) + .collect()) +} + +fn load_private_key(path: &PathBuf) -> Result { + let content = read_to_string(path).context("could not read private SSH key file")?; + let key = PrivateKey::from_openssh(content)?; + + Ok(key) +} + +/* fn get_ssh_key(key_file_path: &PathBuf) -> Result { let content = read_to_string(key_file_path).context("could not read private SSH key file")?; let key = PrivateKey::from_openssh(content)?; @@ -29,3 +86,4 @@ fn get_ssh_key(key_file_path: &PathBuf) -> Result { Ok(key) } +*/ From 549fe5fc575e077d0808e685b76920f91993a33b Mon Sep 17 00:00:00 2001 From: Fabian Weik Date: Sun, 25 Jan 2026 11:16:40 +0100 Subject: [PATCH 09/26] send public keys to server and get back index --- README.org | 2 +- client/src/connection/ssh/mod.rs | 79 +++++++++++++++++++++----------- client/src/connection/video.rs | 8 ++-- server/src/connection/ssh/mod.rs | 46 +++++++++++++------ 4 files changed, 91 insertions(+), 44 deletions(-) diff --git a/README.org b/README.org index d315cc8..164578c 100644 --- a/README.org +++ b/README.org @@ -55,7 +55,7 @@ sudo zypper install gstreamer gstreamer-plugins-base gstreamer-plugins-good ** Server -The must be running. +The server must be running. To install it, first build (see later in this document) and then copy it to your reMarkable. diff --git a/client/src/connection/ssh/mod.rs b/client/src/connection/ssh/mod.rs index 7b0a4b1..0517612 100644 --- a/client/src/connection/ssh/mod.rs +++ b/client/src/connection/ssh/mod.rs @@ -1,12 +1,12 @@ use std::{env::home_dir, fs::read_to_string, path::PathBuf}; use anyhow::{Context, Error, anyhow}; -use ssh_key::PrivateKey; +use itertools::Itertools; +use ssh_key::{PrivateKey, PublicKey}; use tracing::{debug, info, trace}; -use crate::config::ClientOptions; - use super::Connection; +use crate::config::ClientOptions; impl Connection { pub async fn authenticate(&mut self, client_options: ClientOptions) -> Result<(), Error> { @@ -15,17 +15,41 @@ impl Connection { info!("found {} private SSH keys to check", keys_to_check.len()); - /* - let key = get_ssh_key(&client_options.ssh_key).context("could not get private SSH key")?; - let pub_key = key.public_key(); - - self.send(pub_key) + let priv_key = self + .find_authorized_key(keys_to_check) .await - .context("could not send public SSH key")?; - */ + .context("could not find an authorized private key")?; + + info!( + "the private SSH key '{}' with algorithm '{:?}' is authorized", + priv_key.comment(), + priv_key.algorithm(), + ); Ok(()) } + + async fn find_authorized_key( + &mut self, + keys_to_check: Vec, + ) -> Result { + let pub_keys_to_check = keys_to_check + .iter() + .map(|priv_key| priv_key.public_key()) + .cloned() + .collect_vec(); + + self.send(&pub_keys_to_check) + .await + .context("could not send over all public keys to check")?; + + let authorized_key_index: usize = self + .receive() + .await + .context("could not receive authorized key index")?; + + Ok(keys_to_check[authorized_key_index].clone()) + } } fn get_keys_to_check(private_key_path: &Option) -> Result, Error> { @@ -61,9 +85,17 @@ fn get_keys_to_check(private_key_path: &Option) -> Result) -> Result Result { - let content = read_to_string(path).context("could not read private SSH key file")?; - let key = PrivateKey::from_openssh(content)?; - - Ok(key) -} - -/* -fn get_ssh_key(key_file_path: &PathBuf) -> Result { - let content = read_to_string(key_file_path).context("could not read private SSH key file")?; - let key = PrivateKey::from_openssh(content)?; - - debug!("read private key: {:?}", key); + let content = read_to_string(path).context(format!( + "could not read private SSH key file {}", + path.to_string_lossy() + ))?; + let key = PrivateKey::from_openssh(content).context(format!( + "could not parse private key from file {}", + path.to_string_lossy() + ))?; Ok(key) } -*/ diff --git a/client/src/connection/video.rs b/client/src/connection/video.rs index f16cf57..64d550f 100644 --- a/client/src/connection/video.rs +++ b/client/src/connection/video.rs @@ -1,7 +1,7 @@ use anyhow::{Context, Error}; use futures::stream::StreamExt; use lz4_flex::decompress_size_prepended; -use tracing::debug; +use tracing::trace; use crate::display::Display; @@ -23,7 +23,7 @@ impl VideoConnection { pub async fn run(&mut self) -> Result<(), Error> { loop { - debug!("attempting to read data from TCP stream"); + trace!("attempting to read data from TCP stream"); let compressed_frame = self .conn @@ -33,7 +33,7 @@ impl VideoConnection { .context("TCP stream was closed")? .context("could not read from TCP stream")?; - debug!( + trace!( "read one compressed frame from TCP stream ({} bytes)", compressed_frame.len(), ); @@ -41,7 +41,7 @@ impl VideoConnection { let frame = decompress_size_prepended(&compressed_frame) .context("could not decompress received frame")?; - debug!("decompressed: {} bytes", frame.len()); + trace!("decompressed: {} bytes", frame.len()); self.display .push_frame(frame) diff --git a/server/src/connection/ssh/mod.rs b/server/src/connection/ssh/mod.rs index dde1c10..236963c 100644 --- a/server/src/connection/ssh/mod.rs +++ b/server/src/connection/ssh/mod.rs @@ -1,6 +1,6 @@ use std::{path::PathBuf, str::FromStr}; -use anyhow::{Context, Error, anyhow}; +use anyhow::{Context, Error}; use ssh_key::{AuthorizedKeys, PublicKey, authorized_keys::Entry}; use tracing::debug; @@ -8,28 +8,48 @@ use super::Connection; impl Connection { pub async fn authenticate(&mut self) -> Result<(), Error> { + let pub_key = self + .find_authorized_key() + .await + .context("could not find an authorized key")?; + + debug!("received public SSH key: {:?}", pub_key); + + Ok(()) + } + + async fn find_authorized_key(&mut self) -> Result { let authorized_keys = get_authorized_keys().context("could not get authorized keys")?; - let pub_key: PublicKey = self + let pub_keys: Vec = self .receive() .await - .context("could not receive public SSH key")?; + .context("could not receive public SSH keys to check for authorization")?; - let pub_key_authorized = authorized_keys + let authorized_key_index = pub_keys .iter() - .any(|authorized_key| authorized_key.public_key() == &pub_key); - - if !pub_key_authorized { - return Err(anyhow!("public key is not authorized")); - } - - debug!("received public SSH key: {:?}", pub_key); + .enumerate() + .filter_map(|(index, pub_key)| { + is_key_authorized(pub_key, &authorized_keys).then_some(index) + }) + .next() + .context("none of the provided public keys is authorized")?; + + self.send(&authorized_key_index) + .await + .context("could not send index of authorized key")?; - Ok(()) + Ok(pub_keys[authorized_key_index].clone()) } } -fn get_authorized_keys<'a>() -> Result, Error> { +fn is_key_authorized(pub_key: &PublicKey, authorized_keys: &Vec) -> bool { + authorized_keys + .iter() + .any(|authorized_key| authorized_key.public_key() == pub_key) +} + +fn get_authorized_keys() -> Result, Error> { AuthorizedKeys::read_file( PathBuf::from_str("/home/root/.ssh/authorized_keys") .context("could not build path of authorized keys")?, From 2fe47ba1aa452fd7c3f1934db04ca2c8eb6c99ef Mon Sep 17 00:00:00 2001 From: Fabian Weik Date: Sun, 25 Jan 2026 13:17:29 +0100 Subject: [PATCH 10/26] sign a message to complete auth --- Cargo.lock | 92 ++++++++++++++++++++++++++----- client/Cargo.toml | 1 + client/src/connection/mod.rs | 27 +++++++-- client/src/connection/ssh/keys.rs | 68 +++++++++++++++++++++++ client/src/connection/ssh/mod.rs | 92 ++++++++++--------------------- server/Cargo.toml | 2 + server/src/connection/mod.rs | 26 +++++++-- server/src/connection/ssh/mod.rs | 33 ++++++++++- 8 files changed, 253 insertions(+), 88 deletions(-) create mode 100644 client/src/connection/ssh/keys.rs diff --git a/Cargo.lock b/Cargo.lock index 10f110b..b2d8abe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -306,7 +306,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", - "rand_core", + "rand_core 0.6.4", "subtle", "zeroize", ] @@ -439,7 +439,7 @@ dependencies = [ "generic-array", "group", "pkcs8", - "rand_core", + "rand_core 0.6.4", "sec1", "subtle", "zeroize", @@ -511,7 +511,7 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" dependencies = [ - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -663,6 +663,18 @@ dependencies = [ "wasi", ] +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + [[package]] name = "gio-sys" version = "0.21.5" @@ -738,7 +750,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ "ff", - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -1116,7 +1128,7 @@ dependencies = [ "num-integer", "num-iter", "num-traits", - "rand", + "rand 0.8.5", "smallvec", "zeroize", ] @@ -1216,7 +1228,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "primeorder", - "rand_core", + "rand_core 0.6.4", "sha2", ] @@ -1343,6 +1355,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "radium" version = "0.7.0" @@ -1355,8 +1373,18 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", ] [[package]] @@ -1366,7 +1394,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", ] [[package]] @@ -1375,7 +1413,16 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", ] [[package]] @@ -1410,6 +1457,7 @@ dependencies = [ "review-server", "serde", "serde_json", + "ssh-encoding", "ssh-key", "test-log", "tokio", @@ -1429,8 +1477,10 @@ dependencies = [ "libremarkable", "lz4_flex", "procfs", + "rand 0.9.2", "serde", "serde_json", + "ssh-encoding", "ssh-key", "test-log", "tokio", @@ -1462,7 +1512,7 @@ dependencies = [ "num-traits", "pkcs1", "pkcs8", - "rand_core", + "rand_core 0.6.4", "sha2", "signature", "spki", @@ -1614,7 +1664,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -1678,6 +1728,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb9242b9ef4108a78e8cd1a2c98e193ef372437f8c22be363075233321dd4a15" dependencies = [ "base64ct", + "bytes", "pem-rfc7468", "sha2", ] @@ -1694,7 +1745,7 @@ dependencies = [ "p256", "p384", "p521", - "rand_core", + "rand_core 0.6.4", "rsa", "sec1", "serde", @@ -2031,6 +2082,15 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + [[package]] name = "wasm-bindgen" version = "0.2.108" @@ -2227,6 +2287,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" + [[package]] name = "wyz" version = "0.5.1" diff --git a/client/Cargo.toml b/client/Cargo.toml index b680d9b..18be503 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -23,3 +23,4 @@ tokio-util = { version = "0.7.18", features = ["io", "io-util", "codec"] } review-server = { path = "../server" } futures = "0.3.31" ssh-key = { version = "0.6.7", features = ["p256", "p384", "p521", "dsa", "ed25519", "rsa", "serde"] } +ssh-encoding = { version = "0.2.0", features = ["bytes"] } diff --git a/client/src/connection/mod.rs b/client/src/connection/mod.rs index 5508829..aa9750b 100644 --- a/client/src/connection/mod.rs +++ b/client/src/connection/mod.rs @@ -11,7 +11,10 @@ use review_server::{ }; use serde::{Serialize, de::DeserializeOwned}; use tokio::net::TcpStream; -use tokio_util::codec::{Framed, LengthDelimitedCodec}; +use tokio_util::{ + bytes::{Bytes, BytesMut}, + codec::{Framed, LengthDelimitedCodec}, +}; use tracing::info; use crate::config::ClientOptions; @@ -79,11 +82,11 @@ impl Connection { .next() .await .context(format!( - "connection closed before message of type {} was sent", + "connection closed before message of type {} was received", type_name::(), ))? .context(format!( - "could not send message of type {}", + "could not receive message of type {}", type_name::() ))?; @@ -95,7 +98,15 @@ impl Connection { Ok(stream_config) } - pub async fn send(&mut self, value: &T) -> Result<(), Error> { + async fn receive_raw(&mut self) -> Result { + self.framed + .next() + .await + .context(format!("connection closed before raw message was received",))? + .context(format!("could not receive raw message")) + } + + async fn send(&mut self, value: &T) -> Result<(), Error> { let msg = serde_json::to_vec(value) .context(format!("could not serialize type {}", type_name::()))?; @@ -108,4 +119,12 @@ impl Connection { )) .map(|_| ()) } + + async fn send_raw(&mut self, msg: Bytes) -> Result<(), Error> { + self.framed + .send(msg) + .await + .context(format!("could not send raw message")) + .map(|_| ()) + } } diff --git a/client/src/connection/ssh/keys.rs b/client/src/connection/ssh/keys.rs new file mode 100644 index 0000000..21c102d --- /dev/null +++ b/client/src/connection/ssh/keys.rs @@ -0,0 +1,68 @@ +use std::{env::home_dir, fs::read_to_string, path::PathBuf}; + +use anyhow::{Context, Error, anyhow}; +use ssh_key::PrivateKey; +use tracing::{debug, trace}; + +pub fn get_keys_to_check(private_key_path: &Option) -> Result, Error> { + if let Some(private_key_path) = private_key_path { + debug!( + "private key at {} explicitly defined, only loading this key", + private_key_path.to_string_lossy() + ); + return Ok(vec![ + load_private_key(private_key_path) + .context("could not load explicitly specified private SSH key")?, + ]); + } + debug!("no private key explicitly defined, loading keys from .ssh directory"); + + let ssh_directory = home_dir() + .context("could not get home directory")? + .join(".ssh"); + + if !ssh_directory.exists() || ssh_directory.is_file() { + return Err(anyhow!( + "SSH directory '{}' does not exist or is a file", + ssh_directory.to_string_lossy() + )); + } + + let dir_entries = ssh_directory + .read_dir() + .context("could not read the SSH directory")?; + + Ok(dir_entries + .filter_map(|entry| entry.ok()) + .filter_map(|entry| { + let name = entry.file_name(); + let name = name.to_str()?; + let path = ssh_directory.join(name); + + trace!( + "attemting to parse {} as a private SSH key", + path.to_string_lossy() + ); + let private_key = load_private_key(&path).ok()?; + trace!( + "successfully parsed {} as a private SSH key", + path.to_string_lossy() + ); + + Some(private_key) + }) + .collect()) +} + +fn load_private_key(path: &PathBuf) -> Result { + let content = read_to_string(path).context(format!( + "could not read private SSH key file {}", + path.to_string_lossy() + ))?; + let key = PrivateKey::from_openssh(content).context(format!( + "could not parse private key from file {}", + path.to_string_lossy() + ))?; + + Ok(key) +} diff --git a/client/src/connection/ssh/mod.rs b/client/src/connection/ssh/mod.rs index 0517612..4260f52 100644 --- a/client/src/connection/ssh/mod.rs +++ b/client/src/connection/ssh/mod.rs @@ -1,12 +1,15 @@ -use std::{env::home_dir, fs::read_to_string, path::PathBuf}; +mod keys; -use anyhow::{Context, Error, anyhow}; +use anyhow::{Context, Error}; use itertools::Itertools; +use ssh_encoding::Encode; use ssh_key::{PrivateKey, PublicKey}; -use tracing::{debug, info, trace}; +use tokio_util::bytes::BytesMut; +use tracing::info; use super::Connection; use crate::config::ClientOptions; +use keys::get_keys_to_check; impl Connection { pub async fn authenticate(&mut self, client_options: ClientOptions) -> Result<(), Error> { @@ -26,6 +29,10 @@ impl Connection { priv_key.algorithm(), ); + self.sign_request(&priv_key) + .await + .context("could not sign the requested message")?; + Ok(()) } @@ -50,67 +57,26 @@ impl Connection { Ok(keys_to_check[authorized_key_index].clone()) } -} - -fn get_keys_to_check(private_key_path: &Option) -> Result, Error> { - if let Some(private_key_path) = private_key_path { - debug!( - "private key at {} explicitly defined, only loading this key", - private_key_path.to_string_lossy() - ); - return Ok(vec![ - load_private_key(private_key_path) - .context("could not load explicitly specified private SSH key")?, - ]); - } - debug!("no private key explicitly defined, loading keys from .ssh directory"); - let ssh_directory = home_dir() - .context("could not get home directory")? - .join(".ssh"); + async fn sign_request(&mut self, priv_key: &PrivateKey) -> Result<(), Error> { + let message = self + .receive_raw() + .await + .context("could not receive message to sign")? + .to_vec(); + + let signature = priv_key + .sign("review", ssh_key::HashAlg::Sha512, &message) + .context("could not sign the requested message")?; + + let mut encoded_signature = vec![]; + signature + .encode(&mut encoded_signature) + .context("could not encode signature")?; + self.send_raw(encoded_signature.into()) + .await + .context("could not send out encoded signature")?; - if !ssh_directory.exists() || ssh_directory.is_file() { - return Err(anyhow!( - "SSH directory '{}' does not exist or is a file", - ssh_directory.to_string_lossy() - )); + Ok(()) } - - let dir_entries = ssh_directory - .read_dir() - .context("could not read the SSH directory")?; - - Ok(dir_entries - .filter_map(|entry| entry.ok()) - .filter_map(|entry| { - let name = entry.file_name(); - let name = name.to_str()?; - let path = ssh_directory.join(name); - - trace!( - "attemting to parse {} as a private SSH key", - path.to_string_lossy() - ); - let private_key = load_private_key(&path).ok()?; - trace!( - "successfully parsed {} as a private SSH key", - path.to_string_lossy() - ); - - Some(private_key) - }) - .collect()) -} - -fn load_private_key(path: &PathBuf) -> Result { - let content = read_to_string(path).context(format!( - "could not read private SSH key file {}", - path.to_string_lossy() - ))?; - let key = PrivateKey::from_openssh(content).context(format!( - "could not parse private key from file {}", - path.to_string_lossy() - ))?; - - Ok(key) } diff --git a/server/Cargo.toml b/server/Cargo.toml index 8414dc6..1506974 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -21,3 +21,5 @@ futures = "0.3.31" procfs = "0.18.0" itertools = "0.14.0" ssh-key = { version = "0.6.7", features = ["p256", "p384", "p521", "dsa", "ed25519", "rsa", "serde", "std"] } +ssh-encoding = { version = "0.2.0", features = ["bytes"] } +rand = "0.9.2" diff --git a/server/src/connection/mod.rs b/server/src/connection/mod.rs index 7696fae..47cf819 100644 --- a/server/src/connection/mod.rs +++ b/server/src/connection/mod.rs @@ -5,9 +5,9 @@ use std::any::type_name; use anyhow::{Context, Error}; use futures::{StreamExt, sink::SinkExt}; -use serde::de::DeserializeOwned; -use serde::{Deserialize, Serialize}; +use serde::{Serialize, de::DeserializeOwned}; use tokio::net::TcpStream; +use tokio_util::bytes::{Bytes, BytesMut}; use tokio_util::codec::{Framed, LengthDelimitedCodec}; use tracing::info; @@ -61,11 +61,11 @@ impl Connection { .next() .await .context(format!( - "connection closed before message of type {} was sent", + "connection closed before message of type {} was received", type_name::(), ))? .context(format!( - "could not send message of type {}", + "could not receive message of type {}", type_name::() ))?; @@ -77,7 +77,15 @@ impl Connection { Ok(stream_config) } - pub async fn send(&mut self, value: &T) -> Result<(), Error> { + async fn receive_raw(&mut self) -> Result { + self.framed + .next() + .await + .context(format!("connection closed before raw message was received",))? + .context(format!("could not receive raw message")) + } + + async fn send(&mut self, value: &T) -> Result<(), Error> { let msg = serde_json::to_vec(value) .context(format!("could not serialize type {}", type_name::()))?; @@ -90,4 +98,12 @@ impl Connection { )) .map(|_| ()) } + + async fn send_raw(&mut self, msg: Bytes) -> Result<(), Error> { + self.framed + .send(msg) + .await + .context(format!("could not send raw message")) + .map(|_| ()) + } } diff --git a/server/src/connection/ssh/mod.rs b/server/src/connection/ssh/mod.rs index 236963c..2867883 100644 --- a/server/src/connection/ssh/mod.rs +++ b/server/src/connection/ssh/mod.rs @@ -1,13 +1,14 @@ use std::{path::PathBuf, str::FromStr}; use anyhow::{Context, Error}; -use ssh_key::{AuthorizedKeys, PublicKey, authorized_keys::Entry}; +use ssh_encoding::{Decode, Reader}; +use ssh_key::{AuthorizedKeys, PublicKey, SshSig, authorized_keys::Entry}; use tracing::debug; use super::Connection; impl Connection { - pub async fn authenticate(&mut self) -> Result<(), Error> { + pub async fn authenticate(&mut self) -> Result { let pub_key = self .find_authorized_key() .await @@ -15,7 +16,11 @@ impl Connection { debug!("received public SSH key: {:?}", pub_key); - Ok(()) + self.request_signature(&pub_key) + .await + .context("could not get signature from client")?; + + Ok(pub_key) } async fn find_authorized_key(&mut self) -> Result { @@ -41,6 +46,28 @@ impl Connection { Ok(pub_keys[authorized_key_index].clone()) } + + async fn request_signature(&mut self, pub_key: &PublicKey) -> Result<(), Error> { + let message: [u8; 128] = rand::random(); + self.send_raw(message.to_vec().into()) + .await + .context("could not send message to sign")?; + + let encoded_signature = self + .receive_raw() + .await + .context("culd not get signed message back")? + .to_vec(); + let mut encoded_signature = encoded_signature.as_slice(); + let signature = + SshSig::decode(&mut encoded_signature).context("could not decode signature")?; + + pub_key + .verify("review", &message, &signature) + .context("could not verify signature")?; + + Ok(()) + } } fn is_key_authorized(pub_key: &PublicKey, authorized_keys: &Vec) -> bool { From 31bc0a3b1c725a55d5da24d163d17c019b182f9c Mon Sep 17 00:00:00 2001 From: Fabian Weik Date: Sun, 25 Jan 2026 22:18:35 +0100 Subject: [PATCH 11/26] WIP send all public keys with signatures --- client/src/connection/ssh/mod.rs | 77 ++++++++++-------- server/src/connection/ssh/mod.rs | 134 +++++++++++++++++++++---------- server/src/lib.rs | 2 + 3 files changed, 137 insertions(+), 76 deletions(-) diff --git a/client/src/connection/ssh/mod.rs b/client/src/connection/ssh/mod.rs index 4260f52..a5f124e 100644 --- a/client/src/connection/ssh/mod.rs +++ b/client/src/connection/ssh/mod.rs @@ -1,11 +1,10 @@ mod keys; -use anyhow::{Context, Error}; +use anyhow::{Context, Error, anyhow}; use itertools::Itertools; -use ssh_encoding::Encode; -use ssh_key::{PrivateKey, PublicKey}; -use tokio_util::bytes::BytesMut; -use tracing::info; +use review_server::connection::ssh::{PublicKeyAndSignature, PublicKeys}; +use ssh_key::{HashAlg, PrivateKey}; +use tracing::{info, warn}; use super::Connection; use crate::config::ClientOptions; @@ -13,13 +12,18 @@ use keys::get_keys_to_check; impl Connection { pub async fn authenticate(&mut self, client_options: ClientOptions) -> Result<(), Error> { + let token = self + .receive_raw() + .await + .context("could not receive authentification token")? + .to_vec(); + let keys_to_check = get_keys_to_check(&client_options.ssh_key) .context("could not get private keys to check")?; - info!("found {} private SSH keys to check", keys_to_check.len()); let priv_key = self - .find_authorized_key(keys_to_check) + .find_authorized_key(keys_to_check, token) .await .context("could not find an authorized private key")?; @@ -29,23 +33,48 @@ impl Connection { priv_key.algorithm(), ); - self.sign_request(&priv_key) - .await - .context("could not sign the requested message")?; - Ok(()) } async fn find_authorized_key( &mut self, keys_to_check: Vec, + token: Vec, ) -> Result { let pub_keys_to_check = keys_to_check .iter() - .map(|priv_key| priv_key.public_key()) - .cloned() + .filter_map(|priv_key| { + let pub_key = priv_key.public_key().clone(); + let signature = match priv_key.sign("review", HashAlg::Sha512, &token) { + Ok(signature) => signature, + Err(err) => { + warn!( + "private key '{}' could not sign authentification token: {:?}", + priv_key.comment(), + err, + ); + return None; + } + }; + + Some((pub_key, signature)) + }) .collect_vec(); + if pub_keys_to_check.len() == 0 { + return Err(anyhow!( + "none of the private SSH keys could sign the authentification token" + )); + } + + let pub_keys_to_check = PublicKeys { + keys_and_signatures: pub_keys_to_check + .iter() + .map(PublicKeyAndSignature::try_from) + .collect::>() + .context("could not encode the public keys and signatures")?, + }; + self.send(&pub_keys_to_check) .await .context("could not send over all public keys to check")?; @@ -57,26 +86,4 @@ impl Connection { Ok(keys_to_check[authorized_key_index].clone()) } - - async fn sign_request(&mut self, priv_key: &PrivateKey) -> Result<(), Error> { - let message = self - .receive_raw() - .await - .context("could not receive message to sign")? - .to_vec(); - - let signature = priv_key - .sign("review", ssh_key::HashAlg::Sha512, &message) - .context("could not sign the requested message")?; - - let mut encoded_signature = vec![]; - signature - .encode(&mut encoded_signature) - .context("could not encode signature")?; - self.send_raw(encoded_signature.into()) - .await - .context("could not send out encoded signature")?; - - Ok(()) - } } diff --git a/server/src/connection/ssh/mod.rs b/server/src/connection/ssh/mod.rs index 2867883..8d70b56 100644 --- a/server/src/connection/ssh/mod.rs +++ b/server/src/connection/ssh/mod.rs @@ -1,41 +1,106 @@ use std::{path::PathBuf, str::FromStr}; use anyhow::{Context, Error}; -use ssh_encoding::{Decode, Reader}; -use ssh_key::{AuthorizedKeys, PublicKey, SshSig, authorized_keys::Entry}; +use serde::{Deserialize, Serialize}; +use ssh_encoding::{Decode, Encode}; +use ssh_key::{AuthorizedKeys, PublicKey, Signature, SshSig, authorized_keys::Entry}; use tracing::debug; use super::Connection; +// use later when switching to bson +#[derive(Debug, Serialize, Deserialize)] +pub struct AuthentificationToken { + pub token: Vec, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct PublicKeys { + pub keys_and_signatures: Vec, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct PublicKeyAndSignature { + key: Vec, + signature: Vec, +} + +impl TryFrom<&(PublicKey, SshSig)> for PublicKeyAndSignature { + type Error = Error; + + fn try_from((pub_key, signature): &(PublicKey, SshSig)) -> Result { + let encoded_key = pub_key.to_bytes().context(format!( + "could not encode public key '{}'", + pub_key.comment(), + ))?; + + let mut encoded_signature = vec![]; + signature + .encode(&mut encoded_signature) + .context("could not encode signature")?; + + Ok(PublicKeyAndSignature { + key: encoded_key, + signature: encoded_signature, + }) + } +} + +impl TryInto<(PublicKey, SshSig)> for &PublicKeyAndSignature { + type Error = Error; + + fn try_into(self) -> Result<(PublicKey, SshSig), Self::Error> { + let pub_key = PublicKey::from_bytes(&self.key) + .context("could not decode public key from ssh string")?; + + let mut signature_bytes = self.signature.as_slice(); + let signature = + SshSig::decode(&mut signature_bytes).context("could not decode signature")?; + + Ok((pub_key, signature)) + } +} + impl Connection { pub async fn authenticate(&mut self) -> Result { - let pub_key = self - .find_authorized_key() + let token: [u8; 128] = rand::random(); + let token = token.to_vec(); + let token_message = AuthentificationToken { + token: token.clone(), + }; + self.send(&token_message) .await - .context("could not find an authorized key")?; + .context("could not send authentification token to client")?; - debug!("received public SSH key: {:?}", pub_key); - - self.request_signature(&pub_key) + let pub_key = self + .find_authorized_key(&token) .await - .context("could not get signature from client")?; + .context("could not find an authorized key")?; Ok(pub_key) } - async fn find_authorized_key(&mut self) -> Result { + async fn find_authorized_key(&mut self, token: &Vec) -> Result { let authorized_keys = get_authorized_keys().context("could not get authorized keys")?; - let pub_keys: Vec = self + let pub_keys: PublicKeys = self .receive() .await - .context("could not receive public SSH keys to check for authorization")?; + .context("could not receive public keys")?; + + let keys_and_signatures = pub_keys + .keys_and_signatures + .iter() + .map(|key_and_signature| key_and_signature.try_into()) + .collect::, Error>>() + .context("could not parse public keys and signatures")?; - let authorized_key_index = pub_keys + let authorized_key_index = keys_and_signatures .iter() .enumerate() - .filter_map(|(index, pub_key)| { - is_key_authorized(pub_key, &authorized_keys).then_some(index) + .filter_map(|(index, (pub_key, signature))| { + is_key_authorized_and_authentic(pub_key, token, signature, &authorized_keys) + .then_some(index) }) .next() .context("none of the provided public keys is authorized")?; @@ -44,36 +109,23 @@ impl Connection { .await .context("could not send index of authorized key")?; - Ok(pub_keys[authorized_key_index].clone()) - } - - async fn request_signature(&mut self, pub_key: &PublicKey) -> Result<(), Error> { - let message: [u8; 128] = rand::random(); - self.send_raw(message.to_vec().into()) - .await - .context("could not send message to sign")?; - - let encoded_signature = self - .receive_raw() - .await - .context("culd not get signed message back")? - .to_vec(); - let mut encoded_signature = encoded_signature.as_slice(); - let signature = - SshSig::decode(&mut encoded_signature).context("could not decode signature")?; - - pub_key - .verify("review", &message, &signature) - .context("could not verify signature")?; - - Ok(()) + Ok(keys_and_signatures[authorized_key_index].0.clone()) } } -fn is_key_authorized(pub_key: &PublicKey, authorized_keys: &Vec) -> bool { - authorized_keys +fn is_key_authorized_and_authentic( + pub_key: &PublicKey, + token: &Vec, + signature: &SshSig, + authorized_keys: &Vec, +) -> bool { + let authorized = authorized_keys .iter() - .any(|authorized_key| authorized_key.public_key() == pub_key) + .any(|authorized_key| authorized_key.public_key() == pub_key); + + let authentic = pub_key.verify("review", token, signature).is_ok(); + + authorized && authentic } fn get_authorized_keys() -> Result, Error> { diff --git a/server/src/lib.rs b/server/src/lib.rs index ee65768..3c7bc44 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -1,2 +1,4 @@ pub mod config; +pub mod connection; +mod framebuffer; pub mod version; From 83da923e53e52dc0e55d770d4b0b2a0764e0a7df Mon Sep 17 00:00:00 2001 From: Fabian Weik Date: Mon, 26 Jan 2026 21:09:57 +0100 Subject: [PATCH 12/26] only check signatures, dont compare public keys --- client/src/connection/ssh/mod.rs | 8 ++++++- server/src/connection/ssh/mod.rs | 36 ++++++++++++++++++-------------- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/client/src/connection/ssh/mod.rs b/client/src/connection/ssh/mod.rs index a5f124e..e9c5f73 100644 --- a/client/src/connection/ssh/mod.rs +++ b/client/src/connection/ssh/mod.rs @@ -4,7 +4,7 @@ use anyhow::{Context, Error, anyhow}; use itertools::Itertools; use review_server::connection::ssh::{PublicKeyAndSignature, PublicKeys}; use ssh_key::{HashAlg, PrivateKey}; -use tracing::{info, warn}; +use tracing::{debug, error, info, warn}; use super::Connection; use crate::config::ClientOptions; @@ -57,6 +57,12 @@ impl Connection { } }; + debug!("token (length: {}), {:?}", token.len(), token); + if let Err(err) = pub_key.verify("review", &token, &signature) { + error!("problem verifying signature: {:?}", err); + } + debug!("public key {} successfully signed token", pub_key.comment()); + Some((pub_key, signature)) }) .collect_vec(); diff --git a/server/src/connection/ssh/mod.rs b/server/src/connection/ssh/mod.rs index 8d70b56..ed92658 100644 --- a/server/src/connection/ssh/mod.rs +++ b/server/src/connection/ssh/mod.rs @@ -3,12 +3,11 @@ use std::{path::PathBuf, str::FromStr}; use anyhow::{Context, Error}; use serde::{Deserialize, Serialize}; use ssh_encoding::{Decode, Encode}; -use ssh_key::{AuthorizedKeys, PublicKey, Signature, SshSig, authorized_keys::Entry}; +use ssh_key::{AuthorizedKeys, PublicKey, SshSig, authorized_keys::Entry}; use tracing::debug; use super::Connection; -// use later when switching to bson #[derive(Debug, Serialize, Deserialize)] pub struct AuthentificationToken { pub token: Vec, @@ -95,12 +94,12 @@ impl Connection { .collect::, Error>>() .context("could not parse public keys and signatures")?; - let authorized_key_index = keys_and_signatures + let (authorized_key_index, pub_key) = keys_and_signatures .iter() .enumerate() - .filter_map(|(index, (pub_key, signature))| { - is_key_authorized_and_authentic(pub_key, token, signature, &authorized_keys) - .then_some(index) + .filter_map(|(index, (_, signature))| { + get_authorized_key_matching_signature(token, signature, &authorized_keys) + .map(|pub_key| (index, pub_key)) }) .next() .context("none of the provided public keys is authorized")?; @@ -109,23 +108,28 @@ impl Connection { .await .context("could not send index of authorized key")?; - Ok(keys_and_signatures[authorized_key_index].0.clone()) + Ok(pub_key) } } -fn is_key_authorized_and_authentic( - pub_key: &PublicKey, +fn get_authorized_key_matching_signature( token: &Vec, signature: &SshSig, authorized_keys: &Vec, -) -> bool { - let authorized = authorized_keys +) -> Option { + authorized_keys .iter() - .any(|authorized_key| authorized_key.public_key() == pub_key); - - let authentic = pub_key.verify("review", token, signature).is_ok(); - - authorized && authentic + .filter_map(|entry| { + let pub_key = entry.public_key(); + debug!("public key: {:?}, signature: {:?}", pub_key, signature); + debug!("token (length: {}), {:?}", token.len(), token); + pub_key + .verify("review", token, signature) + .ok() + .map(|_| pub_key) + }) + .cloned() + .next() } fn get_authorized_keys() -> Result, Error> { From d2014745407e094221c9e89e67e32a93f74b733d Mon Sep 17 00:00:00 2001 From: Fabian Weik Date: Mon, 26 Jan 2026 21:17:30 +0100 Subject: [PATCH 13/26] also receive token in struct instead of raw bytes --- client/src/connection/ssh/mod.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/client/src/connection/ssh/mod.rs b/client/src/connection/ssh/mod.rs index e9c5f73..f91e3bf 100644 --- a/client/src/connection/ssh/mod.rs +++ b/client/src/connection/ssh/mod.rs @@ -2,7 +2,7 @@ mod keys; use anyhow::{Context, Error, anyhow}; use itertools::Itertools; -use review_server::connection::ssh::{PublicKeyAndSignature, PublicKeys}; +use review_server::connection::ssh::{AuthentificationToken, PublicKeyAndSignature, PublicKeys}; use ssh_key::{HashAlg, PrivateKey}; use tracing::{debug, error, info, warn}; @@ -12,18 +12,17 @@ use keys::get_keys_to_check; impl Connection { pub async fn authenticate(&mut self, client_options: ClientOptions) -> Result<(), Error> { - let token = self - .receive_raw() + let token: AuthentificationToken = self + .receive() .await - .context("could not receive authentification token")? - .to_vec(); + .context("could not receive authentification token")?; let keys_to_check = get_keys_to_check(&client_options.ssh_key) .context("could not get private keys to check")?; info!("found {} private SSH keys to check", keys_to_check.len()); let priv_key = self - .find_authorized_key(keys_to_check, token) + .find_authorized_key(keys_to_check, token.token) .await .context("could not find an authorized private key")?; From 38914d7d35cd98c5b6f65fd2c68f805b2992d760 Mon Sep 17 00:00:00 2001 From: Fabian Weik Date: Mon, 26 Jan 2026 21:19:31 +0100 Subject: [PATCH 14/26] switch to bson again --- Cargo.lock | 146 ++++++++++++++++++++++++++++++----- client/Cargo.toml | 2 +- client/src/connection/mod.rs | 4 +- server/Cargo.toml | 2 +- server/src/connection/mod.rs | 4 +- 5 files changed, 131 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b2d8abe..01d5ac3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,19 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.4", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.4" @@ -109,6 +122,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "base64ct" version = "1.8.3" @@ -148,6 +167,28 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bson" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3f109694c4f45353972af96bf97d8a057f82e2d6e496457f4d135b9867a518c" +dependencies = [ + "ahash", + "base64", + "bitvec", + "getrandom 0.3.4", + "hex", + "indexmap", + "js-sys", + "rand 0.9.2", + "serde", + "serde_bytes", + "simdutf8", + "thiserror 2.0.17", + "time", + "uuid", +] + [[package]] name = "bumpalo" version = "3.19.1" @@ -357,6 +398,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "deranged" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +dependencies = [ + "powerfmt", +] + [[package]] name = "digest" version = "0.10.7" @@ -670,9 +720,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi", "wasip2", + "wasm-bindgen", ] [[package]] @@ -1133,6 +1185,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "num-conv" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" + [[package]] name = "num-integer" version = "0.1.46" @@ -1286,6 +1344,12 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -1447,6 +1511,7 @@ name = "review-client" version = "0.1.0" dependencies = [ "anyhow", + "bson", "clap", "futures", "gstreamer", @@ -1456,7 +1521,6 @@ dependencies = [ "lz4_flex", "review-server", "serde", - "serde_json", "ssh-encoding", "ssh-key", "test-log", @@ -1471,6 +1535,7 @@ name = "review-server" version = "0.1.0" dependencies = [ "anyhow", + "bson", "clap", "futures", "itertools", @@ -1479,7 +1544,6 @@ dependencies = [ "procfs", "rand 0.9.2", "serde", - "serde_json", "ssh-encoding", "ssh-key", "test-log", @@ -1578,6 +1642,16 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_bytes" +version = "0.11.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" +dependencies = [ + "serde", + "serde_core", +] + [[package]] name = "serde_core" version = "1.0.228" @@ -1598,19 +1672,6 @@ dependencies = [ "syn", ] -[[package]] -name = "serde_json" -version = "1.0.149" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - [[package]] name = "serde_spanned" version = "1.0.4" @@ -1673,6 +1734,12 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "slab" version = "0.4.11" @@ -1883,6 +1950,37 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "time" +version = "0.3.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9da98b7d9b7dad93488a84b8248efc35352b0b2657397d4167e7ad67e5d535e5" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + +[[package]] +name = "time-macros" +version = "0.2.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78cc610bac2dcee56805c99642447d4c5dbde4d01f752ffea0199aee1f601dc4" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "tokio" version = "1.49.0" @@ -2058,6 +2156,18 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "uuid" +version = "1.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f" +dependencies = [ + "getrandom 0.3.4", + "js-sys", + "serde_core", + "wasm-bindgen", +] + [[package]] name = "valuable" version = "0.1.1" @@ -2327,9 +2437,3 @@ name = "zeroize" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" - -[[package]] -name = "zmij" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd8f3f50b848df28f887acb68e41201b5aea6bc8a8dacc00fb40635ff9a72fea" diff --git a/client/Cargo.toml b/client/Cargo.toml index 18be503..d50e637 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -9,7 +9,6 @@ edition = "2024" tokio = { version = "1.49.0", features = ["macros", "net", "rt-multi-thread"] } clap = { version = "4.5", features = ["derive"] } serde = { version = "1.0.228", features = ["derive"] } -serde_json = "1.0.149" anyhow = "1.0" itertools = "0.14.0" tracing = "0.1.44" @@ -24,3 +23,4 @@ review-server = { path = "../server" } futures = "0.3.31" ssh-key = { version = "0.6.7", features = ["p256", "p384", "p521", "dsa", "ed25519", "rsa", "serde"] } ssh-encoding = { version = "0.2.0", features = ["bytes"] } +bson = { version = "3.1.0", features = ["serde"] } diff --git a/client/src/connection/mod.rs b/client/src/connection/mod.rs index aa9750b..b1f54a1 100644 --- a/client/src/connection/mod.rs +++ b/client/src/connection/mod.rs @@ -90,7 +90,7 @@ impl Connection { type_name::() ))?; - let stream_config = serde_json::from_slice(&msg).context(format!( + let stream_config = bson::deserialize_from_slice(&msg).context(format!( "could not deserialize message of type {}", type_name::(), ))?; @@ -107,7 +107,7 @@ impl Connection { } async fn send(&mut self, value: &T) -> Result<(), Error> { - let msg = serde_json::to_vec(value) + let msg = bson::serialize_to_vec(value) .context(format!("could not serialize type {}", type_name::()))?; self.framed diff --git a/server/Cargo.toml b/server/Cargo.toml index 1506974..7955e7d 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -10,7 +10,6 @@ tokio = { version = "1.49.0", features = ["macros", "net", "rt-multi-thread", "t libremarkable = { version = "0.7", features = ["input"], default-features = false } clap = { version = "4.5", features = ["derive"] } serde = { version = "1.0.228", features = ["derive"] } -serde_json = "1.0.149" anyhow = "1.0" tracing = "0.1.44" tracing-subscriber = "0.3.22" @@ -23,3 +22,4 @@ itertools = "0.14.0" ssh-key = { version = "0.6.7", features = ["p256", "p384", "p521", "dsa", "ed25519", "rsa", "serde", "std"] } ssh-encoding = { version = "0.2.0", features = ["bytes"] } rand = "0.9.2" +bson = { version = "3.1.0", features = ["serde"] } diff --git a/server/src/connection/mod.rs b/server/src/connection/mod.rs index 47cf819..3b7343e 100644 --- a/server/src/connection/mod.rs +++ b/server/src/connection/mod.rs @@ -69,7 +69,7 @@ impl Connection { type_name::() ))?; - let stream_config = serde_json::from_slice(&msg).context(format!( + let stream_config = bson::deserialize_from_slice(&msg).context(format!( "could not deserialize message of type {}", type_name::(), ))?; @@ -86,7 +86,7 @@ impl Connection { } async fn send(&mut self, value: &T) -> Result<(), Error> { - let msg = serde_json::to_vec(value) + let msg = bson::serialize_to_vec(value) .context(format!("could not serialize type {}", type_name::()))?; self.framed From 0b247494802f49fcfea456344a1574e8fa7dfd70 Mon Sep 17 00:00:00 2001 From: Fabian Weik Date: Mon, 26 Jan 2026 21:46:23 +0100 Subject: [PATCH 15/26] send authorized key index wrapped in message --- client/src/connection/ssh/mod.rs | 8 +++++--- server/src/connection/ssh/mod.rs | 11 ++++++++++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/client/src/connection/ssh/mod.rs b/client/src/connection/ssh/mod.rs index f91e3bf..09164b3 100644 --- a/client/src/connection/ssh/mod.rs +++ b/client/src/connection/ssh/mod.rs @@ -2,7 +2,9 @@ mod keys; use anyhow::{Context, Error, anyhow}; use itertools::Itertools; -use review_server::connection::ssh::{AuthentificationToken, PublicKeyAndSignature, PublicKeys}; +use review_server::connection::ssh::{ + AuthentificationToken, AuthorizedPublicKey, PublicKeyAndSignature, PublicKeys, +}; use ssh_key::{HashAlg, PrivateKey}; use tracing::{debug, error, info, warn}; @@ -84,11 +86,11 @@ impl Connection { .await .context("could not send over all public keys to check")?; - let authorized_key_index: usize = self + let authorized_key: AuthorizedPublicKey = self .receive() .await .context("could not receive authorized key index")?; - Ok(keys_to_check[authorized_key_index].clone()) + Ok(keys_to_check[authorized_key.index].clone()) } } diff --git a/server/src/connection/ssh/mod.rs b/server/src/connection/ssh/mod.rs index ed92658..92e96b2 100644 --- a/server/src/connection/ssh/mod.rs +++ b/server/src/connection/ssh/mod.rs @@ -60,6 +60,11 @@ impl TryInto<(PublicKey, SshSig)> for &PublicKeyAndSignature { } } +#[derive(Debug, Serialize, Deserialize)] +pub struct AuthorizedPublicKey { + pub index: usize, +} + impl Connection { pub async fn authenticate(&mut self) -> Result { let token: [u8; 128] = rand::random(); @@ -104,7 +109,11 @@ impl Connection { .next() .context("none of the provided public keys is authorized")?; - self.send(&authorized_key_index) + let authorized_key_message = AuthorizedPublicKey { + index: authorized_key_index, + }; + + self.send(&authorized_key_message) .await .context("could not send index of authorized key")?; From 3a641421d7d137607c1af7a00c3bd694f812e0a1 Mon Sep 17 00:00:00 2001 From: Fabian Weik Date: Mon, 26 Jan 2026 22:00:55 +0100 Subject: [PATCH 16/26] only send the signatures --- client/src/connection/ssh/mod.rs | 33 ++++---------- server/src/connection/ssh/mod.rs | 77 +++++++++++++------------------- 2 files changed, 40 insertions(+), 70 deletions(-) diff --git a/client/src/connection/ssh/mod.rs b/client/src/connection/ssh/mod.rs index 09164b3..03855a8 100644 --- a/client/src/connection/ssh/mod.rs +++ b/client/src/connection/ssh/mod.rs @@ -3,10 +3,10 @@ mod keys; use anyhow::{Context, Error, anyhow}; use itertools::Itertools; use review_server::connection::ssh::{ - AuthentificationToken, AuthorizedPublicKey, PublicKeyAndSignature, PublicKeys, + AuthentificationToken, AuthorizedPublicKey, SIGNATURE_NAMESPACE, Signatures, }; use ssh_key::{HashAlg, PrivateKey}; -use tracing::{debug, error, info, warn}; +use tracing::{info, warn}; use super::Connection; use crate::config::ClientOptions; @@ -42,47 +42,32 @@ impl Connection { keys_to_check: Vec, token: Vec, ) -> Result { - let pub_keys_to_check = keys_to_check + let signatures = keys_to_check .iter() .filter_map(|priv_key| { - let pub_key = priv_key.public_key().clone(); - let signature = match priv_key.sign("review", HashAlg::Sha512, &token) { - Ok(signature) => signature, + match priv_key.sign(SIGNATURE_NAMESPACE, HashAlg::Sha512, &token) { + Ok(signature) => Some(signature), Err(err) => { warn!( "private key '{}' could not sign authentification token: {:?}", priv_key.comment(), err, ); - return None; + None } - }; - - debug!("token (length: {}), {:?}", token.len(), token); - if let Err(err) = pub_key.verify("review", &token, &signature) { - error!("problem verifying signature: {:?}", err); } - debug!("public key {} successfully signed token", pub_key.comment()); - - Some((pub_key, signature)) }) .collect_vec(); - if pub_keys_to_check.len() == 0 { + if signatures.len() == 0 { return Err(anyhow!( "none of the private SSH keys could sign the authentification token" )); } - let pub_keys_to_check = PublicKeys { - keys_and_signatures: pub_keys_to_check - .iter() - .map(PublicKeyAndSignature::try_from) - .collect::>() - .context("could not encode the public keys and signatures")?, - }; + let signatures: Signatures = signatures.try_into()?; - self.send(&pub_keys_to_check) + self.send(&signatures) .await .context("could not send over all public keys to check")?; diff --git a/server/src/connection/ssh/mod.rs b/server/src/connection/ssh/mod.rs index 92e96b2..a4d3997 100644 --- a/server/src/connection/ssh/mod.rs +++ b/server/src/connection/ssh/mod.rs @@ -4,59 +4,52 @@ use anyhow::{Context, Error}; use serde::{Deserialize, Serialize}; use ssh_encoding::{Decode, Encode}; use ssh_key::{AuthorizedKeys, PublicKey, SshSig, authorized_keys::Entry}; -use tracing::debug; use super::Connection; +pub const SIGNATURE_NAMESPACE: &str = "review"; + #[derive(Debug, Serialize, Deserialize)] pub struct AuthentificationToken { pub token: Vec, } #[derive(Debug, Serialize, Deserialize)] -pub struct PublicKeys { - pub keys_and_signatures: Vec, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct PublicKeyAndSignature { - key: Vec, - signature: Vec, +pub struct Signatures { + pub encoded_signatures: Vec>, } -impl TryFrom<&(PublicKey, SshSig)> for PublicKeyAndSignature { +impl TryFrom> for Signatures { type Error = Error; - fn try_from((pub_key, signature): &(PublicKey, SshSig)) -> Result { - let encoded_key = pub_key.to_bytes().context(format!( - "could not encode public key '{}'", - pub_key.comment(), - ))?; + fn try_from(signatures: Vec) -> Result { + let encoded_signatures = signatures + .iter() + .map(|signature| -> Result<_, Error> { + let mut encoded_signature = vec![]; + signature + .encode(&mut encoded_signature) + .context("could not encode signature")?; - let mut encoded_signature = vec![]; - signature - .encode(&mut encoded_signature) - .context("could not encode signature")?; + Ok(encoded_signature) + }) + .collect::>()?; - Ok(PublicKeyAndSignature { - key: encoded_key, - signature: encoded_signature, - }) + Ok(Self { encoded_signatures }) } } -impl TryInto<(PublicKey, SshSig)> for &PublicKeyAndSignature { +impl TryInto> for Signatures { type Error = Error; - fn try_into(self) -> Result<(PublicKey, SshSig), Self::Error> { - let pub_key = PublicKey::from_bytes(&self.key) - .context("could not decode public key from ssh string")?; - - let mut signature_bytes = self.signature.as_slice(); - let signature = - SshSig::decode(&mut signature_bytes).context("could not decode signature")?; - - Ok((pub_key, signature)) + fn try_into(self) -> Result, Self::Error> { + self.encoded_signatures + .iter() + .map(|signature_bytes| { + let mut signature_bytes = signature_bytes.as_slice(); + SshSig::decode(&mut signature_bytes).context("could not decode signature") + }) + .collect() } } @@ -87,22 +80,16 @@ impl Connection { async fn find_authorized_key(&mut self, token: &Vec) -> Result { let authorized_keys = get_authorized_keys().context("could not get authorized keys")?; - let pub_keys: PublicKeys = self + let signatures: Signatures = self .receive() .await .context("could not receive public keys")?; + let signatures: Vec = signatures.try_into()?; - let keys_and_signatures = pub_keys - .keys_and_signatures - .iter() - .map(|key_and_signature| key_and_signature.try_into()) - .collect::, Error>>() - .context("could not parse public keys and signatures")?; - - let (authorized_key_index, pub_key) = keys_and_signatures + let (authorized_key_index, pub_key) = signatures .iter() .enumerate() - .filter_map(|(index, (_, signature))| { + .filter_map(|(index, signature)| { get_authorized_key_matching_signature(token, signature, &authorized_keys) .map(|pub_key| (index, pub_key)) }) @@ -130,10 +117,8 @@ fn get_authorized_key_matching_signature( .iter() .filter_map(|entry| { let pub_key = entry.public_key(); - debug!("public key: {:?}, signature: {:?}", pub_key, signature); - debug!("token (length: {}), {:?}", token.len(), token); pub_key - .verify("review", token, signature) + .verify(SIGNATURE_NAMESPACE, token, signature) .ok() .map(|_| pub_key) }) From c6aef3052f7c6801054b0ffd7bef04044b49c2b5 Mon Sep 17 00:00:00 2001 From: Fabian Weik Date: Mon, 26 Jan 2026 22:13:38 +0100 Subject: [PATCH 17/26] describe authorization in readme --- README.org | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/README.org b/README.org index 164578c..981df1e 100644 --- a/README.org +++ b/README.org @@ -18,7 +18,11 @@ Based on [[https://github.com/rien/reStream][reStream]]. #+END_HTML Please note that this project is still under heavy development. -If you run this program, anyone on your network could connect to your reMarkable and stream the contents of your screen. +The server will open a port (6680 per default) on your reMarkable tablet. +It authorizes incoming connections based via SSH. +I am not a cybersecurity expert, please DON'T rely on the correctnes of the implementation. +Also, the content of the connection is NOT encrypted. +Refer to the SSH section. #+BEGIN_HTML

@@ -31,10 +35,12 @@ If you run this program, anyone on your network could connect to your reMarkable

#+END_HTML -* Dependencies +* Prequisites You need +- A private SSH key that was authorized by the reMarkable (=ssh-copy-id=) + - Gstreamer - Gstreamer Plugins Base - Gstreamer Plugins Good @@ -139,3 +145,16 @@ dagger call build-server --source . export --path review-server.arm.static sudo zypper in gstreamer-devel gstreamer-plugins-base-devel #+end_src +* SSH + +SSH is only used for authorization, not encryption. + +** Authentification + +1. The server send a randomly generated 128 byte authorization token to the client. +2. The client signs this token with either the explicitly specified private SSH key, + or all the private SSH keys in the default SSH directory (=~/.ssh=). + Then it sends the signatures to the server. +3. The server verifies each one of the signatures with each authorized keys in the default authorized keys file (=/home/root/.ssh/authorized_keys=), until one of the authorized keys matches the signature. + +You can explicitly set the private SSH key to be used via a command line option or an environment variable. From b4bc7134d3e44e716389fda1479c4bd4522e7b8a Mon Sep 17 00:00:00 2001 From: Fabian Weik Date: Mon, 26 Jan 2026 22:17:42 +0100 Subject: [PATCH 18/26] clippy fix --- client/src/config.rs | 18 ++++++------------ client/src/connection/mod.rs | 6 +++--- client/src/connection/ssh/mod.rs | 2 +- client/src/display/mod.rs | 3 --- server/src/connection/mod.rs | 6 +++--- server/src/connection/ssh/mod.rs | 3 +-- server/src/framebuffer/process.rs | 4 +--- 7 files changed, 15 insertions(+), 27 deletions(-) diff --git a/client/src/config.rs b/client/src/config.rs index 23250e9..73997cb 100644 --- a/client/src/config.rs +++ b/client/src/config.rs @@ -136,14 +136,10 @@ fn resolve_with( "read environment variable '{}': {:?}", variable_name, env_string, ); - parse(env_string.expect(&format!( - "could not get environment varialbe '{}'", - variable_name, - ))) - .expect(&format!( - "could not parse environemt variable '{}'", - variable_name, - )) + parse(env_string.unwrap_or_else(|_| panic!("could not get environment varialbe '{}'", + variable_name))) + .unwrap_or_else(|_| panic!("could not parse environemt variable '{}'", + variable_name)) } fn resolve_with_optional( @@ -165,10 +161,8 @@ fn resolve_with_optional( "read environment variable '{}': {}", variable_name, env_string, ); - return Some(parse(env_string).expect(&format!( - "could not parse environemt variable '{}'", - variable_name, - ))); + return Some(parse(env_string).unwrap_or_else(|_| panic!("could not parse environemt variable '{}'", + variable_name))); } None diff --git a/client/src/connection/mod.rs b/client/src/connection/mod.rs index b1f54a1..8daa5bd 100644 --- a/client/src/connection/mod.rs +++ b/client/src/connection/mod.rs @@ -102,8 +102,8 @@ impl Connection { self.framed .next() .await - .context(format!("connection closed before raw message was received",))? - .context(format!("could not receive raw message")) + .context("connection closed before raw message was received".to_string())? + .context("could not receive raw message".to_string()) } async fn send(&mut self, value: &T) -> Result<(), Error> { @@ -124,7 +124,7 @@ impl Connection { self.framed .send(msg) .await - .context(format!("could not send raw message")) + .context("could not send raw message".to_string()) .map(|_| ()) } } diff --git a/client/src/connection/ssh/mod.rs b/client/src/connection/ssh/mod.rs index 03855a8..649a5e4 100644 --- a/client/src/connection/ssh/mod.rs +++ b/client/src/connection/ssh/mod.rs @@ -59,7 +59,7 @@ impl Connection { }) .collect_vec(); - if signatures.len() == 0 { + if signatures.is_empty() { return Err(anyhow!( "none of the private SSH keys could sign the authentification token" )); diff --git a/client/src/display/mod.rs b/client/src/display/mod.rs index 67e06b7..f2fcb55 100644 --- a/client/src/display/mod.rs +++ b/client/src/display/mod.rs @@ -1,12 +1,9 @@ -use super::config::*; -use std::{io::Read as _, thread::sleep, time::Duration}; use anyhow::{Context, Error}; use gstreamer_app::AppSrc; use gstreamer_video::VideoFormat; use review_server::config::device::{PixelFormat, VideoConfig}; -use tracing::{debug, info}; use gstreamer::{Pipeline, prelude::*}; diff --git a/server/src/connection/mod.rs b/server/src/connection/mod.rs index 3b7343e..3b26408 100644 --- a/server/src/connection/mod.rs +++ b/server/src/connection/mod.rs @@ -81,8 +81,8 @@ impl Connection { self.framed .next() .await - .context(format!("connection closed before raw message was received",))? - .context(format!("could not receive raw message")) + .context("connection closed before raw message was received".to_string())? + .context("could not receive raw message".to_string()) } async fn send(&mut self, value: &T) -> Result<(), Error> { @@ -103,7 +103,7 @@ impl Connection { self.framed .send(msg) .await - .context(format!("could not send raw message")) + .context("could not send raw message".to_string()) .map(|_| ()) } } diff --git a/server/src/connection/ssh/mod.rs b/server/src/connection/ssh/mod.rs index a4d3997..587e924 100644 --- a/server/src/connection/ssh/mod.rs +++ b/server/src/connection/ssh/mod.rs @@ -122,8 +122,7 @@ fn get_authorized_key_matching_signature( .ok() .map(|_| pub_key) }) - .cloned() - .next() + .next().cloned() } fn get_authorized_keys() -> Result, Error> { diff --git a/server/src/framebuffer/process.rs b/server/src/framebuffer/process.rs index f4e7882..a89c88d 100644 --- a/server/src/framebuffer/process.rs +++ b/server/src/framebuffer/process.rs @@ -1,9 +1,7 @@ use std::fs::File; use anyhow::{Context, Error, anyhow}; -use itertools::Itertools; use procfs::process::{MMapPath, MemoryMap, Process, all_processes}; -use tracing::trace; pub fn get_xochitl_memory_file() -> Result<(File, usize), Error> { let process = get_process().context("could not get xochitl process")?; @@ -47,7 +45,7 @@ fn get_framebuffer_offset_in_process_memory(process: &Process) -> Result Date: Mon, 26 Jan 2026 22:20:56 +0100 Subject: [PATCH 19/26] utilize send and receive raw functions --- client/src/connection/video.rs | 6 ++---- server/src/connection/video.rs | 5 ++--- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/client/src/connection/video.rs b/client/src/connection/video.rs index 64d550f..3224cb5 100644 --- a/client/src/connection/video.rs +++ b/client/src/connection/video.rs @@ -27,11 +27,9 @@ impl VideoConnection { let compressed_frame = self .conn - .framed - .next() + .receive_raw() .await - .context("TCP stream was closed")? - .context("could not read from TCP stream")?; + .context("could not reveive next frame")?; trace!( "read one compressed frame from TCP stream ({} bytes)", diff --git a/server/src/connection/video.rs b/server/src/connection/video.rs index dbe63d1..13bf886 100644 --- a/server/src/connection/video.rs +++ b/server/src/connection/video.rs @@ -49,10 +49,9 @@ impl VideoConnection { ); self.conn - .framed - .send(encoded_buffer.into()) + .send_raw(encoded_buffer.into()) .await - .context("could not write frame to the stream")?; + .context("could not send next frame")?; debug!("wrote the data to the output stream"); } From d158c0c68b60dd1aff5faf9135ba964e4954115a Mon Sep 17 00:00:00 2001 From: Fabian Weik Date: Tue, 27 Jan 2026 19:29:58 +0100 Subject: [PATCH 20/26] use OS for seeding random auth token --- server/src/connection/ssh/mod.rs | 33 ++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/server/src/connection/ssh/mod.rs b/server/src/connection/ssh/mod.rs index 587e924..9daab81 100644 --- a/server/src/connection/ssh/mod.rs +++ b/server/src/connection/ssh/mod.rs @@ -1,6 +1,7 @@ use std::{path::PathBuf, str::FromStr}; use anyhow::{Context, Error}; +use rand::{Rng, SeedableRng, rngs::StdRng}; use serde::{Deserialize, Serialize}; use ssh_encoding::{Decode, Encode}; use ssh_key::{AuthorizedKeys, PublicKey, SshSig, authorized_keys::Entry}; @@ -14,6 +15,18 @@ pub struct AuthentificationToken { pub token: Vec, } +const TOKEN_SIZE: usize = 256; + +impl AuthentificationToken { + fn new() -> Self { + let mut rng = StdRng::from_os_rng(); + let token: [u8; TOKEN_SIZE] = rng.random(); + let token = token.to_vec(); + + Self { token } + } +} + #[derive(Debug, Serialize, Deserialize)] pub struct Signatures { pub encoded_signatures: Vec>, @@ -60,12 +73,8 @@ pub struct AuthorizedPublicKey { impl Connection { pub async fn authenticate(&mut self) -> Result { - let token: [u8; 128] = rand::random(); - let token = token.to_vec(); - let token_message = AuthentificationToken { - token: token.clone(), - }; - self.send(&token_message) + let token = AuthentificationToken::new(); + self.send(&token) .await .context("could not send authentification token to client")?; @@ -77,7 +86,10 @@ impl Connection { Ok(pub_key) } - async fn find_authorized_key(&mut self, token: &Vec) -> Result { + async fn find_authorized_key( + &mut self, + token: &AuthentificationToken, + ) -> Result { let authorized_keys = get_authorized_keys().context("could not get authorized keys")?; let signatures: Signatures = self @@ -109,7 +121,7 @@ impl Connection { } fn get_authorized_key_matching_signature( - token: &Vec, + token: &AuthentificationToken, signature: &SshSig, authorized_keys: &Vec, ) -> Option { @@ -118,11 +130,12 @@ fn get_authorized_key_matching_signature( .filter_map(|entry| { let pub_key = entry.public_key(); pub_key - .verify(SIGNATURE_NAMESPACE, token, signature) + .verify(SIGNATURE_NAMESPACE, &token.token, signature) .ok() .map(|_| pub_key) }) - .next().cloned() + .next() + .cloned() } fn get_authorized_keys() -> Result, Error> { From 4e703e5faddb15d36eecec9c71060d551c9f874b Mon Sep 17 00:00:00 2001 From: Fabian Weik Date: Tue, 27 Jan 2026 19:37:29 +0100 Subject: [PATCH 21/26] add link to documentation for SSH key setup --- README.org | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.org b/README.org index 981df1e..6f9e4bc 100644 --- a/README.org +++ b/README.org @@ -39,7 +39,8 @@ Refer to the SSH section. You need -- A private SSH key that was authorized by the reMarkable (=ssh-copy-id=) +- A private SSH key that was authorized by the reMarkable. + Follow [[https://remarkable.guide/guide/access/ssh.html#ssh-access][this]] manual. - Gstreamer - Gstreamer Plugins Base From 7523e47063f765b503e4d786bea02df04cc6a344 Mon Sep 17 00:00:00 2001 From: Fabian Weik Date: Tue, 27 Jan 2026 19:46:08 +0100 Subject: [PATCH 22/26] warn when impossible to read private key --- client/src/connection/ssh/keys.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/client/src/connection/ssh/keys.rs b/client/src/connection/ssh/keys.rs index 21c102d..ad01885 100644 --- a/client/src/connection/ssh/keys.rs +++ b/client/src/connection/ssh/keys.rs @@ -2,7 +2,7 @@ use std::{env::home_dir, fs::read_to_string, path::PathBuf}; use anyhow::{Context, Error, anyhow}; use ssh_key::PrivateKey; -use tracing::{debug, trace}; +use tracing::{debug, trace, warn}; pub fn get_keys_to_check(private_key_path: &Option) -> Result, Error> { if let Some(private_key_path) = private_key_path { @@ -43,7 +43,23 @@ pub fn get_keys_to_check(private_key_path: &Option) -> Result Date: Tue, 27 Jan 2026 19:48:33 +0100 Subject: [PATCH 23/26] error when no private keys found --- client/src/connection/ssh/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client/src/connection/ssh/mod.rs b/client/src/connection/ssh/mod.rs index 649a5e4..e73f136 100644 --- a/client/src/connection/ssh/mod.rs +++ b/client/src/connection/ssh/mod.rs @@ -42,6 +42,10 @@ impl Connection { keys_to_check: Vec, token: Vec, ) -> Result { + if keys_to_check.is_empty() { + return Err(anyhow!("no private SSH keys to authenticate with")); + } + let signatures = keys_to_check .iter() .filter_map(|priv_key| { @@ -61,7 +65,7 @@ impl Connection { if signatures.is_empty() { return Err(anyhow!( - "none of the private SSH keys could sign the authentification token" + "none of the private SSH keys could sign the authentification token", )); } From b1179af9fdffe69eb2da37ab7a7ee088cd104074 Mon Sep 17 00:00:00 2001 From: Fabian Weik Date: Tue, 27 Jan 2026 19:54:01 +0100 Subject: [PATCH 24/26] rename and clippy fix --- client/src/connection/mod.rs | 2 +- client/src/connection/video.rs | 1 - client/src/main.rs | 4 ++-- server/src/connection/mod.rs | 2 +- server/src/main.rs | 4 ++-- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/client/src/connection/mod.rs b/client/src/connection/mod.rs index 8daa5bd..3cdaa4d 100644 --- a/client/src/connection/mod.rs +++ b/client/src/connection/mod.rs @@ -41,7 +41,7 @@ impl Connection { Ok(Connection { framed }) } - pub async fn exchange_information( + pub async fn initialize_communication( &mut self, client_options: ClientOptions, ) -> Result { diff --git a/client/src/connection/video.rs b/client/src/connection/video.rs index 3224cb5..b16e2e8 100644 --- a/client/src/connection/video.rs +++ b/client/src/connection/video.rs @@ -1,5 +1,4 @@ use anyhow::{Context, Error}; -use futures::stream::StreamExt; use lz4_flex::decompress_size_prepended; use tracing::trace; diff --git a/client/src/main.rs b/client/src/main.rs index 0cf21d9..127f7c5 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -27,9 +27,9 @@ async fn main() -> Result<(), Error> { .context("could not initialize TCP connection")?; let device_config = conn - .exchange_information(client_options.clone()) + .initialize_communication(client_options.clone()) .await - .context("error during initial information exchange")?; + .context("error during initializing the communication")?; let mut video_connection = VideoConnection::new(conn, device_config.video_config) .context("could not initialize video connection")?; diff --git a/server/src/connection/mod.rs b/server/src/connection/mod.rs index 3b26408..09ed721 100644 --- a/server/src/connection/mod.rs +++ b/server/src/connection/mod.rs @@ -26,7 +26,7 @@ impl Connection { Connection { framed } } - pub async fn exchange_information(&mut self) -> Result { + pub async fn initialize_communication(&mut self) -> Result { self.authenticate() .await .context("error while authenticating client")?; diff --git a/server/src/main.rs b/server/src/main.rs index 0a9ea40..dbecbb5 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -69,9 +69,9 @@ impl Server { async fn task(mut conn: Connection) -> Result<(), Error> { let stream_config = conn - .exchange_information() + .initialize_communication() .await - .context("error during initial information exchange")?; + .context("error during initializing the communication")?; let mut video_conn = VideoConnection::new(conn, stream_config) .context("could not initialize video connection")?; From 861e099c44d1ca1fd0ae463484d6972e98867b99 Mon Sep 17 00:00:00 2001 From: Fabian Weik Date: Tue, 27 Jan 2026 20:24:11 +0100 Subject: [PATCH 25/26] clean up clippy warnings --- README.org | 2 -- client/src/config.rs | 23 +++++++++++++---------- client/src/connection/mod.rs | 1 + client/src/display/mod.rs | 5 ++--- server/src/config/device.rs | 1 + server/src/connection/mod.rs | 1 + server/src/connection/ssh/mod.rs | 2 +- server/src/connection/video.rs | 1 - server/src/framebuffer/process.rs | 9 ++++----- server/src/main.rs | 1 - server/src/version/mod.rs | 22 ++++++++++++++-------- 11 files changed, 37 insertions(+), 31 deletions(-) diff --git a/README.org b/README.org index 6f9e4bc..ee48193 100644 --- a/README.org +++ b/README.org @@ -96,7 +96,6 @@ Options: --tcp-port TCP port for video stream (default: 6680) --ssh-key Private SSH key file path. If not specified, attempting all keys in the SSH directory --framerate Framerate (default: 50) - --dark-mode Dark mode - invert colors (default: false) --show-cursor Show cursor (default: false) -h, --help Print help -V, --version Print version @@ -109,7 +108,6 @@ Corresponding keys: - =REMARKABLE_TCP_PORT= - =REMARKABLE_SSH_KEY_PATH= - =REMARKABLE_FRAMERATE= -- =REMARKABLE_DARK_MODE= - =REMARKABLE_SHOW_CURSOR= * Building diff --git a/client/src/config.rs b/client/src/config.rs index 73997cb..64cff55 100644 --- a/client/src/config.rs +++ b/client/src/config.rs @@ -33,8 +33,8 @@ pub struct CliOptions { framerate: Option, /// Dark mode - invert colors (default: false) - #[arg(long, name = "dark-mode")] - dark_mode: bool, + //#[arg(long, name = "dark-mode")] + //dark_mode: bool, /// Show cursor (default: false) #[arg(long, name = "show-cursor")] @@ -45,7 +45,8 @@ pub struct CliOptions { pub struct ClientOptions { pub remarkable_ip: String, pub ssh_key: Option, - pub dark_mode: bool, + // TODO: implement dark mode + // pub dark_mode: bool, pub tcp_port: u16, pub show_cursor: bool, pub framerate: f32, @@ -82,7 +83,7 @@ impl From for ClientOptions { }, DEFAULT_FRAMERATE, ), - dark_mode: resolve_boolean_option(value.dark_mode, "REMARKABLE_DARK_MODE", false), + //dark_mode: resolve_boolean_option(value.dark_mode, "REMARKABLE_DARK_MODE", false), show_cursor: resolve_boolean_option(value.show_cursor, "REMARKABLE_SHOW_CURSOR", false), } } @@ -136,10 +137,11 @@ fn resolve_with( "read environment variable '{}': {:?}", variable_name, env_string, ); - parse(env_string.unwrap_or_else(|_| panic!("could not get environment varialbe '{}'", - variable_name))) - .unwrap_or_else(|_| panic!("could not parse environemt variable '{}'", - variable_name)) + parse( + env_string + .unwrap_or_else(|_| panic!("could not get environment varialbe '{}'", variable_name)), + ) + .unwrap_or_else(|_| panic!("could not parse environemt variable '{}'", variable_name)) } fn resolve_with_optional( @@ -161,8 +163,9 @@ fn resolve_with_optional( "read environment variable '{}': {}", variable_name, env_string, ); - return Some(parse(env_string).unwrap_or_else(|_| panic!("could not parse environemt variable '{}'", - variable_name))); + return Some(parse(env_string).unwrap_or_else(|_| { + panic!("could not parse environemt variable '{}'", variable_name) + })); } None diff --git a/client/src/connection/mod.rs b/client/src/connection/mod.rs index 3cdaa4d..7620644 100644 --- a/client/src/connection/mod.rs +++ b/client/src/connection/mod.rs @@ -120,6 +120,7 @@ impl Connection { .map(|_| ()) } + #[allow(unused)] async fn send_raw(&mut self, msg: Bytes) -> Result<(), Error> { self.framed .send(msg) diff --git a/client/src/display/mod.rs b/client/src/display/mod.rs index f2fcb55..d28bfd1 100644 --- a/client/src/display/mod.rs +++ b/client/src/display/mod.rs @@ -1,5 +1,3 @@ - - use anyhow::{Context, Error}; use gstreamer_app::AppSrc; use gstreamer_video::VideoFormat; @@ -9,7 +7,8 @@ use gstreamer::{Pipeline, prelude::*}; #[derive(Debug)] pub struct Display { - pipeline: Pipeline, + #[allow(unused)] + pipeline: Pipeline, // only for keeping alive (TODO: check if realy needed) appsrc: AppSrc, } diff --git a/server/src/config/device.rs b/server/src/config/device.rs index 3259678..7bae551 100644 --- a/server/src/config/device.rs +++ b/server/src/config/device.rs @@ -52,6 +52,7 @@ pub struct DeviceConfig { } impl DeviceConfig { + #[allow(unused)] pub fn new(version_info: VersionInfo) -> Result { let height = 1872; let width = 1404; diff --git a/server/src/connection/mod.rs b/server/src/connection/mod.rs index 09ed721..b892c12 100644 --- a/server/src/connection/mod.rs +++ b/server/src/connection/mod.rs @@ -77,6 +77,7 @@ impl Connection { Ok(stream_config) } + #[allow(unused)] async fn receive_raw(&mut self) -> Result { self.framed .next() diff --git a/server/src/connection/ssh/mod.rs b/server/src/connection/ssh/mod.rs index 9daab81..691a582 100644 --- a/server/src/connection/ssh/mod.rs +++ b/server/src/connection/ssh/mod.rs @@ -123,7 +123,7 @@ impl Connection { fn get_authorized_key_matching_signature( token: &AuthentificationToken, signature: &SshSig, - authorized_keys: &Vec, + authorized_keys: &[Entry], ) -> Option { authorized_keys .iter() diff --git a/server/src/connection/video.rs b/server/src/connection/video.rs index 13bf886..c466b3d 100644 --- a/server/src/connection/video.rs +++ b/server/src/connection/video.rs @@ -1,7 +1,6 @@ use std::time::Duration; use anyhow::{Context, Error}; -use futures::SinkExt; use lz4_flex::compress_prepend_size; use tokio::time::{MissedTickBehavior, interval}; use tracing::{debug, trace}; diff --git a/server/src/framebuffer/process.rs b/server/src/framebuffer/process.rs index a89c88d..8ad6126 100644 --- a/server/src/framebuffer/process.rs +++ b/server/src/framebuffer/process.rs @@ -23,7 +23,7 @@ fn get_process() -> Result { let process = processes.next().context("no xochitl process found")?; - if let Some(_) = processes.next() { + if processes.next().is_some() { return Err(anyhow!("found more than one xochitl process")); } @@ -37,18 +37,17 @@ fn get_framebuffer_offset_in_process_memory(process: &Process) -> Result Result<(), Error> { diff --git a/server/src/version/mod.rs b/server/src/version/mod.rs index 12be871..ad9c433 100644 --- a/server/src/version/mod.rs +++ b/server/src/version/mod.rs @@ -44,7 +44,7 @@ impl FromStr for HardwareVersion { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub struct FirmwareVersion { pub version: usize, pub major: usize, @@ -54,19 +54,25 @@ pub struct FirmwareVersion { impl PartialOrd for FirmwareVersion { fn partial_cmp(&self, other: &Self) -> Option { - match self.version.partial_cmp(&other.version) { - Some(core::cmp::Ordering::Equal) => {} + Some(self.cmp(other)) + } +} + +impl Ord for FirmwareVersion { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + match self.version.cmp(&other.version) { + core::cmp::Ordering::Equal => {} ord => return ord, } - match self.major.partial_cmp(&other.major) { - Some(core::cmp::Ordering::Equal) => {} + match self.major.cmp(&other.major) { + core::cmp::Ordering::Equal => {} ord => return ord, } - match self.minor.partial_cmp(&other.minor) { - Some(core::cmp::Ordering::Equal) => {} + match self.minor.cmp(&other.minor) { + core::cmp::Ordering::Equal => {} ord => return ord, } - self.patch.partial_cmp(&other.patch) + self.patch.cmp(&other.patch) } } From 77bc59e6d51856469d68764443ff3ba63ac56da3 Mon Sep 17 00:00:00 2001 From: Fabian Weik Date: Tue, 27 Jan 2026 20:43:58 +0100 Subject: [PATCH 26/26] check clippy in pipeline --- dagger/main.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/dagger/main.go b/dagger/main.go index 43d4265..a3593ea 100644 --- a/dagger/main.go +++ b/dagger/main.go @@ -31,6 +31,12 @@ func (m *ReView) CheckAndTestAll(ctx context.Context, source *dagger.Directory) } output = output + "\n\n" + o + o, err = linuxContainer(source).WithExec([]string{"cargo", "clippy", "--", "-D", "warnings"}).Stdout(ctx) + if err != nil { + return o, err + } + output = output + "\n\n" + o + return "ok", nil } @@ -51,6 +57,9 @@ func linuxContainer(source *dagger.Directory) *dagger.Container { return dag.Container(). From("rust:"+RustVersion+"-trixie"). WithExec([]string{"apt", "update"}). + WithExec([]string{ + "rustup", "component", "add", "clippy", + }). WithExec([]string{ "apt", "install", "-y", "libgstreamer1.0-dev",