From eb5ac7b6bb4f11835e2300e3307bc2694a48541c Mon Sep 17 00:00:00 2001 From: EthanShoeDev <13422990+EthanShoeDev@users.noreply.github.com> Date: Mon, 22 Sep 2025 01:14:29 -0400 Subject: [PATCH] server key fn --- apps/mobile/src/lib/query-fns.ts | 4 + .../react-native-uniffi-russh/rust/Cargo.lock | 520 ++++++++++++------ .../rust/uniffi-russh/src/ssh_connection.rs | 97 +++- .../rust/uniffi-russh/src/ssh_shell.rs | 17 - packages/react-native-uniffi-russh/src/api.ts | 25 +- 5 files changed, 453 insertions(+), 210 deletions(-) diff --git a/apps/mobile/src/lib/query-fns.ts b/apps/mobile/src/lib/query-fns.ts index d281a3e..182a687 100644 --- a/apps/mobile/src/lib/query-fns.ts +++ b/apps/mobile/src/lib/query-fns.ts @@ -38,6 +38,10 @@ export const useSshConnMutation = (opts?: { console.log('SSH connect progress event', progressEvent); opts?.onConnectionProgress?.(progressEvent); }, + onServerKey: async (serverKeyInfo) => { + console.log('SSH server key', serverKeyInfo); + return true; + }, abortSignal: AbortSignalTimeout(5_000), }); diff --git a/packages/react-native-uniffi-russh/rust/Cargo.lock b/packages/react-native-uniffi-russh/rust/Cargo.lock index 4ccb3c8..819632f 100644 --- a/packages/react-native-uniffi-russh/rust/Cargo.lock +++ b/packages/react-native-uniffi-russh/rust/Cargo.lock @@ -63,15 +63,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anyhow" -version = "1.0.92" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74f37166d7d48a0284b99dd824694c26119c700b53bf0d1540cdb147dbdaaf13" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "argon2" @@ -85,6 +85,48 @@ dependencies = [ "password-hash", ] +[[package]] +name = "askama" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4744ed2eef2645831b441d8f5459689ade2ab27c854488fbab1fbe94fce1a7" +dependencies = [ + "askama_derive", + "itoa", + "percent-encoding", + "serde", + "serde_json", +] + +[[package]] +name = "askama_derive" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d661e0f57be36a5c14c48f78d09011e67e0cb618f269cca9f2fd8d15b68c46ac" +dependencies = [ + "askama_parser", + "basic-toml", + "memchr", + "proc-macro2", + "quote", + "rustc-hash", + "serde", + "serde_derive", + "syn", +] + +[[package]] +name = "askama_parser" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf315ce6524c857bb129ff794935cf6d42c82a6cff60526fe2a63593de4d0d4f" +dependencies = [ + "memchr", + "serde", + "serde_derive", + "winnow", +] + [[package]] name = "async-compat" version = "0.2.5" @@ -111,9 +153,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "backtrace" @@ -150,9 +192,9 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "basic-toml" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8" +checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a" dependencies = [ "serde", ] @@ -231,34 +273,34 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "camino" -version = "1.1.9" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +checksum = "e1de8bc0aa9e9385ceb3bf0c152e3a9b9544f6c4a912c8ae504e80c1f0368603" dependencies = [ - "serde", + "serde_core", ] [[package]] name = "cargo-platform" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" dependencies = [ "serde", ] [[package]] name = "cargo_metadata" -version = "0.15.4" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" +checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" dependencies = [ "camino", "cargo-platform", "semver", "serde", "serde_json", - "thiserror", + "thiserror 2.0.16", ] [[package]] @@ -272,9 +314,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.37" +version = "1.2.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65193589c6404eb80b450d618eaf9a2cafaaafd57ecce47370519ef674a7bd44" +checksum = "80f41ae168f955c12fb8960b057d70d0ca153fb83182b57d86380443527be7e9" dependencies = [ "find-msvc-tools", "shlex", @@ -328,9 +370,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.20" +version = "4.5.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" +checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae" dependencies = [ "clap_builder", "clap_derive", @@ -338,9 +380,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.20" +version = "4.5.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9" dependencies = [ "anstyle", "clap_lex", @@ -349,9 +391,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.18" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" dependencies = [ "heck", "proc-macro2", @@ -361,9 +403,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "const-oid" @@ -565,6 +607,28 @@ dependencies = [ "syn", ] +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.0", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + [[package]] name = "ff" version = "0.13.1" @@ -583,9 +647,9 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "find-msvc-tools" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d" +checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959" [[package]] name = "flate2" @@ -715,10 +779,22 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi", + "wasi 0.11.1+wasi-snapshot-preview1", "wasm-bindgen", ] +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.7+wasi-0.2.4", +] + [[package]] name = "ghash" version = "0.5.1" @@ -737,9 +813,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "glob" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "goblin" @@ -763,6 +839,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" + [[package]] name = "heck" version = "0.5.0" @@ -832,6 +914,16 @@ dependencies = [ "cc", ] +[[package]] +name = "indexmap" +version = "2.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "inout" version = "0.1.4" @@ -883,15 +975,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "js-sys" -version = "0.3.78" +version = "0.3.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0b063578492ceec17683ef2f8c5e89121fbd0b172cbc280635ab7567db2738" +checksum = "852f13bec5eba4ba9afbeb93fd7c13fe56147f055939ae21c43a29a0ecb2702e" dependencies = [ "once_cell", "wasm-bindgen", @@ -919,10 +1011,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] -name = "log" -version = "0.4.22" +name = "linux-raw-sys" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "md5" @@ -932,25 +1030,9 @@ checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "mime_guess" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" -dependencies = [ - "mime", - "unicase", -] +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "minimal-lexical" @@ -974,7 +1056,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", - "wasi", + "wasi 0.11.1+wasi-snapshot-preview1", "windows-sys 0.59.0", ] @@ -1127,7 +1209,7 @@ dependencies = [ "delegate", "futures", "rand", - "thiserror", + "thiserror 1.0.69", "tokio", "windows", ] @@ -1143,7 +1225,7 @@ dependencies = [ "futures", "log", "rand", - "thiserror", + "thiserror 1.0.69", "tokio", "windows", ] @@ -1159,12 +1241,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - [[package]] name = "pbkdf2" version = "0.12.2" @@ -1184,6 +1260,12 @@ dependencies = [ "base64ct", ] +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + [[package]] name = "pin-project-lite" version = "0.2.16" @@ -1283,22 +1365,28 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.37" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 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 = "rand" version = "0.8.5" @@ -1326,7 +1414,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.16", ] [[package]] @@ -1347,51 +1435,12 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom", + "getrandom 0.2.16", "libc", "untrusted", "windows-sys 0.52.0", ] -[[package]] -name = "rinja" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dc4940d00595430b3d7d5a01f6222b5e5b51395d1120bdb28d854bb8abb17a5" -dependencies = [ - "itoa", - "rinja_derive", -] - -[[package]] -name = "rinja_derive" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d9ed0146aef6e2825f1b1515f074510549efba38d71f4554eec32eb36ba18b" -dependencies = [ - "basic-toml", - "memchr", - "mime", - "mime_guess", - "proc-macro2", - "quote", - "rinja_parser", - "rustc-hash", - "serde", - "syn", -] - -[[package]] -name = "rinja_parser" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f9a866e2e00a7a1fb27e46e9e324a6f7c0e7edc4543cae1d38f4e4a100c610" -dependencies = [ - "memchr", - "nom", - "serde", -] - [[package]] name = "rsa" version = "0.9.8" @@ -1439,7 +1488,7 @@ dependencies = [ "flate2", "futures", "generic-array", - "getrandom", + "getrandom 0.2.16", "hex-literal", "hmac", "home", @@ -1470,7 +1519,7 @@ dependencies = [ "spki", "ssh-encoding", "subtle", - "thiserror", + "thiserror 1.0.69", "tokio", "typenum", "zeroize", @@ -1521,7 +1570,7 @@ dependencies = [ "ed25519-dalek", "elliptic-curve", "futures", - "getrandom", + "getrandom 0.2.16", "hmac", "home", "inout", @@ -1549,7 +1598,7 @@ dependencies = [ "spki", "ssh-encoding", "ssh-key", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-stream", "typenum", @@ -1601,6 +1650,19 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.0", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -1609,9 +1671,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "salsa20" @@ -1633,9 +1695,9 @@ dependencies = [ [[package]] name = "scroll_derive" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f81c2fde025af7e69b1d1420531c8a8811ca898919db177141a85313b1cb932" +checksum = "1783eabc414609e28a5ba76aee5ddd52199f7107a0b24c2e9746a1ecc34a683d" dependencies = [ "proc-macro2", "quote", @@ -1669,27 +1731,38 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.23" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" dependencies = [ "serde", + "serde_core", ] [[package]] name = "serde" -version = "1.0.214" +version = "1.0.226" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +checksum = "0dca6411025b24b60bfa7ec1fe1f8e710ac09782dca409ee8237ba74b51295fd" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.226" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba2ba63999edb9dac981fb34b3e5c0d111a69b0924e253ed29d83f7c99e966a4" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.214" +version = "1.0.226" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +checksum = "8db53ae22f34573731bafa1db20f04027b2d25e02d8205921b569171699cdb33" dependencies = [ "proc-macro2", "quote", @@ -1698,14 +1771,15 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ "itoa", "memchr", "ryu", "serde", + "serde_core", ] [[package]] @@ -1868,9 +1942,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.87" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -1878,28 +1952,61 @@ dependencies = [ ] [[package]] -name = "textwrap" -version = "0.16.1" +name = "tempfile" +version = "3.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" +checksum = "84fa4d11fadde498443cca10fd3ac23c951f0dc59e080e9f4b93d4df4e4eea53" +dependencies = [ + "fastrand", + "getrandom 0.3.3", + "once_cell", + "rustix", + "windows-sys 0.61.0", +] + +[[package]] +name = "textwrap" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" dependencies = [ "smawk", ] [[package]] name = "thiserror" -version = "1.0.67" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3c6efbfc763e64eb85c11c25320f0737cb7364c4b6336db90aa9ebe27a0bbd" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +dependencies = [ + "thiserror-impl 2.0.16", ] [[package]] name = "thiserror-impl" -version = "1.0.67" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b607164372e89797d78b8e23a6d67d5d1038c1c65efd52e1389ef8b77caba2a6" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" dependencies = [ "proc-macro2", "quote", @@ -1975,23 +2082,17 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" -[[package]] -name = "unicase" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" - [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" [[package]] name = "uniffi" -version = "0.29.1" +version = "0.29.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe34585ac0275accf6c284d0080cc2840f3898c551cda869ec291b5a4218712c" +checksum = "c6d968cb62160c11f2573e6be724ef8b1b18a277aededd17033f8a912d73e2b4" dependencies = [ "anyhow", "camino", @@ -2001,6 +2102,7 @@ dependencies = [ "uniffi_build", "uniffi_core", "uniffi_macros", + "uniffi_pipeline", ] [[package]] @@ -2030,39 +2132,42 @@ dependencies = [ "rand", "russh", "russh-keys", - "thiserror", + "thiserror 1.0.69", "tokio", "uniffi", ] [[package]] name = "uniffi_bindgen" -version = "0.29.1" +version = "0.29.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a792af1424cc8b3c43b44c1a6cb7935ed1fbe5584a74f70e8bab9799740266d" +checksum = "f6b39ef1acbe1467d5d210f274fae344cb6f8766339330cb4c9688752899bf6b" dependencies = [ "anyhow", + "askama", "camino", "cargo_metadata", "fs-err", "glob", "goblin", "heck", + "indexmap", "once_cell", - "paste", - "rinja", "serde", + "tempfile", "textwrap", "toml", + "uniffi_internal_macros", "uniffi_meta", + "uniffi_pipeline", "uniffi_udl", ] [[package]] name = "uniffi_build" -version = "0.29.1" +version = "0.29.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00c4138211f2ae951018fcce6a978e1fcd1a47c3fd0bc0d5472a520520060db1" +checksum = "6683e6b665423cddeacd89a3f97312cf400b2fb245a26f197adaf65c45d505b2" dependencies = [ "anyhow", "camino", @@ -2071,9 +2176,9 @@ dependencies = [ [[package]] name = "uniffi_core" -version = "0.29.1" +version = "0.29.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c18baace68a52666d33d12d73ca335ecf27a302202cefb53b1f974512bb72417" +checksum = "c2d990b553d6b9a7ee9c3ae71134674739913d52350b56152b0e613595bb5a6f" dependencies = [ "anyhow", "async-compat", @@ -2084,19 +2189,22 @@ dependencies = [ [[package]] name = "uniffi_internal_macros" -version = "0.29.1" +version = "0.29.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9902d4ed16c65e6c0222241024dd0bfeed07ea3deb7c470eb175e5f5ef406cd" +checksum = "04f4f224becf14885c10e6e400b95cc4d1985738140cb194ccc2044563f8a56b" dependencies = [ + "anyhow", + "indexmap", + "proc-macro2", "quote", "syn", ] [[package]] name = "uniffi_macros" -version = "0.29.1" +version = "0.29.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d82c82ef945c51082d8763635334b994e63e77650f09d0fae6d28dd08b1de83" +checksum = "b481d385af334871d70904e6a5f129be7cd38c18fcf8dd8fd1f646b426a56d58" dependencies = [ "camino", "fs-err", @@ -2111,20 +2219,34 @@ dependencies = [ [[package]] name = "uniffi_meta" -version = "0.29.1" +version = "0.29.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d6027b971c2aa86350dd180aee9819729c7b99bacd381534511ff29d2c09cea" +checksum = "10f817868a3b171bb7bf259e882138d104deafde65684689b4694c846d322491" dependencies = [ "anyhow", "siphasher", "uniffi_internal_macros", + "uniffi_pipeline", +] + +[[package]] +name = "uniffi_pipeline" +version = "0.29.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b147e133ad7824e32426b90bc41fda584363563f2ba747f590eca1fd6fd14e6" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "tempfile", + "uniffi_internal_macros", ] [[package]] name = "uniffi_udl" -version = "0.29.1" +version = "0.29.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52300b7a4ab02dc159a038a13d5bfe27aefbad300d91b0b501b3dda094c1e0a2" +checksum = "caed654fb73da5abbc7a7e9c741532284532ba4762d6fe5071372df22a41730a" dependencies = [ "anyhow", "textwrap", @@ -2161,10 +2283,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] -name = "wasm-bindgen" -version = "0.2.101" +name = "wasi" +version = "0.14.7+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e14915cadd45b529bb8d1f343c4ed0ac1de926144b746e2710f9cd05df6603b" +checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" +dependencies = [ + "wasip2", +] + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab10a69fbd0a177f5f649ad4d8d3305499c42bab9aef2f7ff592d0ec8f833819" dependencies = [ "cfg-if", "once_cell", @@ -2175,9 +2315,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.101" +version = "0.2.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28d1ba982ca7923fd01448d5c30c6864d0a14109560296a162f80f305fb93bb" +checksum = "0bb702423545a6007bbc368fde243ba47ca275e549c8a28617f56f6ba53b1d1c" dependencies = [ "bumpalo", "log", @@ -2189,9 +2329,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.51" +version = "0.4.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca85039a9b469b38336411d6d6ced91f3fc87109a2a27b0c197663f5144dffe" +checksum = "a0b221ff421256839509adbb55998214a70d829d3a28c69b4a6672e9d2a42f67" dependencies = [ "cfg-if", "js-sys", @@ -2202,9 +2342,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.101" +version = "0.2.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c3d463ae3eff775b0c45df9da45d68837702ac35af998361e2c84e7c5ec1b0d" +checksum = "fc65f4f411d91494355917b605e1480033152658d71f722a90647f56a70c88a0" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2212,9 +2352,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.101" +version = "0.2.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bb4ce89b08211f923caf51d527662b75bdc9c9c7aab40f86dcb9fb85ac552aa" +checksum = "ffc003a991398a8ee604a401e194b6b3a39677b3173d6e74495eb51b82e99a32" dependencies = [ "proc-macro2", "quote", @@ -2225,18 +2365,18 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.101" +version = "0.2.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f143854a3b13752c6950862c906306adb27c7e839f7414cec8fea35beab624c1" +checksum = "293c37f4efa430ca14db3721dfbe48d8c33308096bd44d80ebaa775ab71ba1cf" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" -version = "0.3.78" +version = "0.3.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77e4b637749ff0d92b8fad63aa1f7cff3cbe125fd49c175cd6345e7272638b12" +checksum = "fbe734895e869dc429d78c4b433f8d17d95f8d05317440b4fad5ab2d33e596dc" dependencies = [ "js-sys", "wasm-bindgen", @@ -2414,6 +2554,15 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -2478,6 +2627,21 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + [[package]] name = "zerocopy" version = "0.8.27" diff --git a/packages/react-native-uniffi-russh/rust/uniffi-russh/src/ssh_connection.rs b/packages/react-native-uniffi-russh/rust/uniffi-russh/src/ssh_connection.rs index e3781a9..3ce0f45 100644 --- a/packages/react-native-uniffi-russh/rust/uniffi-russh/src/ssh_connection.rs +++ b/packages/react-native-uniffi-russh/rust/uniffi-russh/src/ssh_connection.rs @@ -5,17 +5,17 @@ use tokio::sync::{broadcast, Mutex as AsyncMutex}; use russh::client::{Config, Handle as ClientHandle}; use russh::keys::PrivateKeyWithHashAlg; -use russh::{self, ChannelMsg, Disconnect}; +use russh::{self, client, ChannelMsg, Disconnect}; use crate::private_key::normalize_openssh_ed25519_seed_key; use crate::ssh_shell::{ - append_and_broadcast, Chunk, NoopHandler, ShellSession, ShellSessionInfo, StartShellOptions, - StreamKind, DEFAULT_BROADCAST_CHUNK_CAPACITY, DEFAULT_MAX_CHUNK_SIZE, - DEFAULT_SHELL_RING_BUFFER_CAPACITY, DEFAULT_TERMINAL_MODES, DEFAULT_TERM_COALESCE_MS, - DEFAULT_TERM_COL_WIDTH, DEFAULT_TERM_PIXEL_HEIGHT, DEFAULT_TERM_PIXEL_WIDTH, - DEFAULT_TERM_ROW_HEIGHT, + append_and_broadcast, Chunk, ShellSession, ShellSessionInfo, StartShellOptions, StreamKind, + DEFAULT_BROADCAST_CHUNK_CAPACITY, DEFAULT_MAX_CHUNK_SIZE, DEFAULT_SHELL_RING_BUFFER_CAPACITY, + DEFAULT_TERMINAL_MODES, DEFAULT_TERM_COALESCE_MS, DEFAULT_TERM_COL_WIDTH, + DEFAULT_TERM_PIXEL_HEIGHT, DEFAULT_TERM_PIXEL_WIDTH, DEFAULT_TERM_ROW_HEIGHT, }; use crate::utils::{now_ms, SshError}; +use russh::keys::PublicKeyBase64; use std::sync::atomic::AtomicUsize; use std::{ @@ -23,6 +23,31 @@ use std::{ sync::{atomic::AtomicU64, Mutex}, }; +fn server_public_key_to_info( + host: &str, + port: u16, + remote_ip: Option, + pk: &russh::keys::PublicKey, +) -> ServerPublicKeyInfo { + // Algorithm identifier (e.g., "ssh-ed25519", "rsa-sha2-512") + let algorithm = pk.algorithm().to_string(); + + // Key blob (base64) + let key_base64 = pk.public_key_base64(); + + // Fingerprints via russh-keys/ssh-key helpers + let fingerprint_sha256 = format!("{}", pk.fingerprint(russh::keys::ssh_key::HashAlg::Sha256)); + + ServerPublicKeyInfo { + host: host.to_string(), + port, + remote_ip, + algorithm, + fingerprint_sha256, + key_base64, + } +} + #[derive(Debug, Clone, PartialEq, uniffi::Enum)] pub enum Security { Password { password: String }, @@ -42,6 +67,7 @@ pub struct ConnectOptions { pub connection_details: ConnectionDetails, pub on_connection_progress_callback: Option>, pub on_disconnected_callback: Option>, + pub on_server_key_callback: Arc, } #[derive(Debug, Clone, Copy, PartialEq, uniffi::Enum)] @@ -70,6 +96,22 @@ pub trait ConnectionDisconnectedCallback: Send + Sync { fn on_change(&self, connection_id: String); } +#[derive(Debug, Clone, PartialEq, uniffi::Record)] +pub struct ServerPublicKeyInfo { + pub host: String, + pub port: u16, + pub remote_ip: Option, + pub algorithm: String, + pub fingerprint_sha256: String, // e.g., "SHA256:..." (no padding) + pub key_base64: String, // raw key blob (base64) +} + +#[uniffi::export(with_foreign)] +#[async_trait::async_trait] +pub trait ServerKeyCallback: Send + Sync { + async fn on_change(&self, server_key_info: ServerPublicKeyInfo) -> bool; +} + #[derive(Debug, Clone, PartialEq, uniffi::Record)] pub struct SshConnectionInfo { pub connection_id: String, @@ -79,6 +121,35 @@ pub struct SshConnectionInfo { pub progress_timings: SshConnectionInfoProgressTimings, } +/// Minimal client::Handler with optional server key callback. +pub(crate) struct NoopHandler { + pub on_server_key_callback: Arc, + pub host: String, + pub port: u16, + pub remote_ip: Option, +} +impl client::Handler for NoopHandler { + type Error = SshError; + fn check_server_key( + &mut self, + server_public_key: &russh::keys::PublicKey, + ) -> impl std::future::Future< + Output = std::result::Result::Error>, + > + std::marker::Send { + let cb = self.on_server_key_callback.clone(); + let host = self.host.clone(); + let port = self.port; + let remote_ip = self.remote_ip.clone(); + // Build structured info for UI/decision. + let info = server_public_key_to_info(&host, port, remote_ip, server_public_key); + async move { + // Delegate decision to user callback (async via UniFFI). + let accept = cb.on_change(info).await; + Ok(accept) + } + } +} + #[derive(uniffi::Object)] pub struct SshConnection { pub info: SshConnectionInfo, @@ -315,8 +386,18 @@ pub async fn connect(options: ConnectOptions) -> Result, SshE sl.on_change(SshConnectionProgressEvent::TcpConnected); } let cfg = Arc::new(Config::default()); - let mut handle: ClientHandle = - russh::client::connect_stream(cfg, socket, NoopHandler).await?; + let remote_ip = socket.peer_addr().ok().map(|a| a.ip().to_string()); + let mut handle: ClientHandle = russh::client::connect_stream( + cfg, + socket, + NoopHandler { + on_server_key_callback: options.on_server_key_callback.clone(), + host: options.connection_details.host.clone(), + port: options.connection_details.port, + remote_ip, + }, + ) + .await?; let ssh_handshake_at_ms = now_ms(); if let Some(sl) = options.on_connection_progress_callback.as_ref() { sl.on_change(SshConnectionProgressEvent::SshHandshake); diff --git a/packages/react-native-uniffi-russh/rust/uniffi-russh/src/ssh_shell.rs b/packages/react-native-uniffi-russh/rust/uniffi-russh/src/ssh_shell.rs index 0363bfe..2f2a843 100644 --- a/packages/react-native-uniffi-russh/rust/uniffi-russh/src/ssh_shell.rs +++ b/packages/react-native-uniffi-russh/rust/uniffi-russh/src/ssh_shell.rs @@ -187,23 +187,6 @@ pub(crate) struct Chunk { bytes: Bytes, } -/// Minimal client::Handler. -pub(crate) struct NoopHandler; -impl client::Handler for NoopHandler { - type Error = SshError; - // Accept any server key for now so dev UX isn't blocked. - // TODO: Add known-hosts verification and surface API to control this. - #[allow(unused_variables)] - fn check_server_key( - &mut self, - _server_public_key: &russh::keys::PublicKey, - ) -> impl std::future::Future< - Output = std::result::Result::Error>, - > + std::marker::Send { - std::future::ready(Ok(true)) - } -} - /// ---------- Methods ---------- pub(crate) static DEFAULT_TERMINAL_MODES: &[(russh::Pty, u32)] = &[ (russh::Pty::ECHO, 1), // This will cause the terminal to echo the characters back to the client. diff --git a/packages/react-native-uniffi-russh/src/api.ts b/packages/react-native-uniffi-russh/src/api.ts index a4fc5af..64db8a2 100644 --- a/packages/react-native-uniffi-russh/src/api.ts +++ b/packages/react-native-uniffi-russh/src/api.ts @@ -50,6 +50,10 @@ export type SshConnectionProgress = export type ConnectOptions = ConnectionDetails & { onConnectionProgress?: (status: SshConnectionProgress) => void; onDisconnected?: (connectionId: string) => void; + onServerKey: ( + serverKeyInfo: GeneratedRussh.ServerPublicKeyInfo, + signal?: AbortSignal + ) => Promise; abortSignal?: AbortSignal; }; @@ -339,7 +343,12 @@ function wrapConnection( }; } -async function connect(options: ConnectOptions): Promise { +async function connect({ + onServerKey, + onConnectionProgress, + onDisconnected, + ...options +}: ConnectOptions): Promise { const security = options.security.type === 'password' ? new GeneratedRussh.Security.Password({ @@ -356,19 +365,21 @@ async function connect(options: ConnectOptions): Promise { username: options.username, security, }, - onConnectionProgressCallback: options.onConnectionProgress + onConnectionProgressCallback: onConnectionProgress ? { onChange: (statusEnum) => - options.onConnectionProgress!( - sshConnProgressEnumToLiteral[statusEnum] - ), + onConnectionProgress(sshConnProgressEnumToLiteral[statusEnum]), } : undefined, - onDisconnectedCallback: options.onDisconnected + onDisconnectedCallback: onDisconnected ? { - onChange: (connectionId) => options.onDisconnected!(connectionId), + onChange: (connectionId) => onDisconnected(connectionId), } : undefined, + onServerKeyCallback: { + onChange: (serverKeyInfo) => + onServerKey(serverKeyInfo, options.abortSignal), + }, }, options.abortSignal ? { signal: options.abortSignal } : undefined );