From c38898bf5cbeb22ba002b0b2053fa0020de3dc38 Mon Sep 17 00:00:00 2001 From: rldyourmnd Date: Thu, 3 Sep 2026 00:29:36 +0500 Subject: [PATCH] refactor(crypto): share byte-compatible Ed25519 signing --- AGENTS.md | 6 ++ Cargo.lock | 151 +++++++++++++++++++++----- NOTICE | 5 + libs/hbb_common/Cargo.toml | 1 + libs/hbb_common/src/config.rs | 2 +- libs/hbb_common/src/crypto_sign.rs | 165 +++++++++++++++++++++++++++++ libs/hbb_common/src/lib.rs | 1 + src/client.rs | 3 +- src/common.rs | 3 +- src/custom_server.rs | 2 +- src/hbbs_http/sync.rs | 2 +- src/server.rs | 3 +- 12 files changed, 312 insertions(+), 32 deletions(-) create mode 100644 libs/hbb_common/src/crypto_sign.rs diff --git a/AGENTS.md b/AGENTS.md index a2fbfbbd0..5a5b360ef 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -117,6 +117,12 @@ checks and the path validator used after an explicit download action. That validator accepts release assets from `NDDev-OpenNetwork/nremote` and no other origin; there is no scheduler, release lookup, or background download. +Authentication signing uses `crypto_sign.rs`, byte-identical to the server's +copy at the coordinated migration point. It preserves 64-byte +`seed || public key` storage and `signature || message` wire payloads; the RFC +8032 vector is the compatibility gate. `sodiumoxide` remains for unrelated +`box_` and XSalsa20-Poly1305 `secretbox` protocol operations. + ## Verification ```bash diff --git a/Cargo.lock b/Cargo.lock index b9869e200..ecebafa95 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -45,7 +45,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ - "crypto-common", + "crypto-common 0.1.6", "generic-array", ] @@ -856,6 +856,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" +dependencies = [ + "hybrid-array", +] + [[package]] name = "block-padding" version = "0.3.3" @@ -1244,7 +1253,7 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "crypto-common", + "crypto-common 0.1.6", "inout", "zeroize", ] @@ -1881,6 +1890,15 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + [[package]] name = "ctor-lite" version = "0.1.0" @@ -1922,7 +1940,23 @@ dependencies = [ "cfg-if 1.0.4", "cpufeatures 0.2.12", "curve25519-dalek-derive", - "fiat-crypto", + "fiat-crypto 0.2.9", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5eed333089e2e1c1ac8c6c0398e5e2497b4c9926ca6d0365ed1e099afa5bc23" +dependencies = [ + "cfg-if 1.0.4", + "cpufeatures 0.3.1", + "curve25519-dalek-derive", + "digest 0.11.3", + "fiat-crypto 0.3.0", "rustc_version", "subtle", "zeroize", @@ -2232,12 +2266,22 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", + "block-buffer 0.10.4", "const-oid", - "crypto-common", + "crypto-common 0.1.6", "subtle", ] +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.1", + "crypto-common 0.2.2", +] + [[package]] name = "directories-next" version = "2.0.0" @@ -2523,7 +2567,7 @@ dependencies = [ "sec1", "serde 1.0.229", "sha1", - "sha2", + "sha2 0.10.8", "thiserror 1.0.61", "tokio", "webrtc-util", @@ -2562,7 +2606,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der", - "digest", + "digest 0.10.7", "elliptic-curve", "rfc6979", "signature 2.2.0", @@ -2578,6 +2622,28 @@ dependencies = [ "signature 1.6.4", ] +[[package]] +name = "ed25519" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29fcf32e6c73d1079f83ab4d782de2d81620346a5f38c6237a86a22f8368980a" +dependencies = [ + "signature 3.0.0", +] + +[[package]] +name = "ed25519-dalek" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ebaa1a2bf1290ab3bfe5a7b771d050ebffab2711c19a81691c683a5144a25de" +dependencies = [ + "curve25519-dalek 5.0.0", + "ed25519 3.0.0", + "sha2 0.11.0", + "subtle", + "zeroize", +] + [[package]] name = "either" version = "1.13.0" @@ -2592,7 +2658,7 @@ checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", - "digest", + "digest 0.10.7", "ff", "generic-array", "group", @@ -2871,6 +2937,12 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +[[package]] +name = "fiat-crypto" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" + [[package]] name = "field-offset" version = "0.3.6" @@ -3835,6 +3907,7 @@ dependencies = [ "directories-next", "dirs-next", "dlopen", + "ed25519-dalek", "env_logger 0.11.11", "filetime", "flexi_logger", @@ -3858,7 +3931,7 @@ dependencies = [ "serde 1.0.229", "serde_derive", "serde_json 1.0.151", - "sha2", + "sha2 0.10.8", "smithay-client-toolkit 0.20.0", "socket2 0.3.19", "sodiumoxide", @@ -3952,7 +4025,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest", + "digest 0.10.7", ] [[package]] @@ -4038,6 +4111,15 @@ dependencies = [ "serde_json 1.0.151", ] +[[package]] +name = "hybrid-array" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707114b52a152fa7bdb290cd7cd5912d9467273b6d74e21b8d81aca1f8533f6b" +dependencies = [ + "typenum", +] + [[package]] name = "hyper" version = "1.7.0" @@ -4699,7 +4781,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d" dependencies = [ "cfg-if 1.0.4", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -4934,7 +5016,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ "cfg-if 1.0.4", - "digest", + "digest 0.10.7", ] [[package]] @@ -5497,7 +5579,7 @@ dependencies = [ "serde_derive", "serde_json 1.0.151", "serde_repr", - "sha2", + "sha2 0.10.8", "shared_memory", "shutdown_hooks", "softbuffer", @@ -5564,7 +5646,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -6285,7 +6367,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "primeorder", - "sha2", + "sha2 0.10.8", ] [[package]] @@ -6297,7 +6379,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "primeorder", - "sha2", + "sha2 0.10.8", ] [[package]] @@ -8078,7 +8160,7 @@ checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if 1.0.4", "cpufeatures 0.2.12", - "digest", + "digest 0.10.7", ] [[package]] @@ -8089,7 +8171,18 @@ checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if 1.0.4", "cpufeatures 0.2.12", - "digest", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if 1.0.4", + "cpufeatures 0.3.1", + "digest 0.11.3", ] [[package]] @@ -8182,10 +8275,16 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ - "digest", + "digest 0.10.7", "rand_core 0.6.4", ] +[[package]] +name = "signature" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d567dcbaf0049cb8ac2608a76cd95ff9e4412e1899d389ee400918ca7537f5" + [[package]] name = "simd-adler32" version = "0.3.7" @@ -8336,7 +8435,7 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e26be3acb6c2d9a7aac28482586a7856436af4cfe7100031d219de2d2ecb0028" dependencies = [ - "ed25519", + "ed25519 1.5.3", "libc", "libsodium-sys", "serde 1.0.229", @@ -9222,7 +9321,7 @@ dependencies = [ "hmac", "rand 0.8.8", "sha1", - "sha2", + "sha2 0.10.8", "url", "urlencoding", ] @@ -9453,9 +9552,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.17.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "tz-rs" @@ -9610,7 +9709,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" dependencies = [ - "crypto-common", + "crypto-common 0.1.6", "subtle", ] @@ -10124,7 +10223,7 @@ dependencies = [ "sdp", "serde 1.0.229", "serde_json 1.0.151", - "sha2", + "sha2 0.10.8", "smol_str", "stun", "thiserror 1.0.61", @@ -11327,7 +11426,7 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" dependencies = [ - "curve25519-dalek", + "curve25519-dalek 4.1.3", "rand_core 0.6.4", "serde 1.0.229", "zeroize", diff --git a/NOTICE b/NOTICE index d3b58b8f3..bc9c158aa 100644 --- a/NOTICE +++ b/NOTICE @@ -53,6 +53,11 @@ Modified by NDDev beginning 2026-09-02. Changes so far: * The wire protocol shared with nremote-server is pinned by digest and checked on both sides by scripts/check_protocol_parity.py. + * Authentication signing is moved from an unmaintained wrapper to + `ed25519-dalek`. Persisted device keypairs and signed server messages retain + their existing byte layouts, verified against the RFC 8032 Ed25519 test + vector; malformed stored keypairs are rejected before use. + The rendezvous protocol is unchanged. This client interoperates with servers built from the prior work, and nremote-server interoperates with clients built from it, which is what makes an incremental migration possible. diff --git a/libs/hbb_common/Cargo.toml b/libs/hbb_common/Cargo.toml index 31192c101..98c9fd6db 100644 --- a/libs/hbb_common/Cargo.toml +++ b/libs/hbb_common/Cargo.toml @@ -28,6 +28,7 @@ zstd = "0.13" anyhow = "1.0" futures-util = "0.3" directories-next = "2.0" +ed25519-dalek = "3.0" rand = "0.10" serde_derive = "1.0" serde = "1.0" diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 767919417..0911914c2 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -9,6 +9,7 @@ use std::{ time::{Duration, Instant, SystemTime}, }; +use crate::crypto_sign as sign; use anyhow::{anyhow, Result}; use bytes::Bytes; use rand::RngExt; @@ -17,7 +18,6 @@ use serde as de; use serde_derive::{Deserialize, Serialize}; use serde_json; use sodiumoxide::base64; -use sodiumoxide::crypto::sign; mod permanent_password; diff --git a/libs/hbb_common/src/crypto_sign.rs b/libs/hbb_common/src/crypto_sign.rs new file mode 100644 index 000000000..45edf34ef --- /dev/null +++ b/libs/hbb_common/src/crypto_sign.rs @@ -0,0 +1,165 @@ +//! Ed25519 signing with the byte layout used by existing nremote keys. +//! +//! Secret keys are stored as `seed || public_key` (64 bytes), and signed +//! messages are encoded as `signature || message`. These are the layouts used +//! by the previous signing implementation, so persisted server keys and every +//! client already in the field remain valid. + +use ed25519_dalek::{Signature, Signer, SigningKey, VerifyingKey}; +use std::{convert::TryInto, ops::Deref}; + +pub const PUBLICKEYBYTES: usize = 32; +pub const SECRETKEYBYTES: usize = 64; +pub const SIGNATUREBYTES: usize = 64; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct VerifyError; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct PublicKey(pub [u8; PUBLICKEYBYTES]); + +#[derive(Clone)] +pub struct SecretKey(pub [u8; SECRETKEYBYTES]); + +impl PublicKey { + pub fn from_slice(bytes: &[u8]) -> Option { + let bytes: [u8; PUBLICKEYBYTES] = bytes.try_into().ok()?; + VerifyingKey::from_bytes(&bytes).ok()?; + Some(Self(bytes)) + } +} + +impl Deref for PublicKey { + type Target = [u8; PUBLICKEYBYTES]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl AsRef<[u8]> for PublicKey { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl SecretKey { + pub fn from_slice(bytes: &[u8]) -> Option { + let bytes: [u8; SECRETKEYBYTES] = bytes.try_into().ok()?; + SigningKey::from_keypair_bytes(&bytes).ok()?; + Some(Self(bytes)) + } +} + +impl Deref for SecretKey { + type Target = [u8; SECRETKEYBYTES]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl AsRef<[u8]> for SecretKey { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl Drop for SecretKey { + fn drop(&mut self) { + self.0.fill(0); + } +} + +pub fn gen_keypair() -> (PublicKey, SecretKey) { + let seed = rand::random::<[u8; 32]>(); + let signing_key = SigningKey::from_bytes(&seed); + let public_key = PublicKey(signing_key.verifying_key().to_bytes()); + let secret_key = SecretKey(signing_key.to_keypair_bytes()); + (public_key, secret_key) +} + +pub fn sign_detached(message: &[u8], secret_key: &SecretKey) -> Signature { + // A SecretKey created through `from_slice` or `gen_keypair` has already + // proved that its public half matches its seed. Reconstructing from the + // seed keeps signing infallible, matching the established API. + let seed: [u8; 32] = secret_key.0[..32] + .try_into() + .expect("the seed is a fixed-size slice"); + SigningKey::from_bytes(&seed).sign(message) +} + +pub fn sign(message: &[u8], secret_key: &SecretKey) -> Vec { + let signature = sign_detached(message, secret_key); + let mut signed = Vec::with_capacity(SIGNATUREBYTES + message.len()); + signed.extend_from_slice(&signature.to_bytes()); + signed.extend_from_slice(message); + signed +} + +pub fn verify(signed: &[u8], public_key: &PublicKey) -> Result, VerifyError> { + if signed.len() < SIGNATUREBYTES { + return Err(VerifyError); + } + let verifying_key = VerifyingKey::from_bytes(&public_key.0).map_err(|_| VerifyError)?; + let signature = Signature::from_slice(&signed[..SIGNATUREBYTES]).map_err(|_| VerifyError)?; + let message = &signed[SIGNATUREBYTES..]; + verifying_key + .verify_strict(message, &signature) + .map_err(|_| VerifyError)?; + Ok(message.to_vec()) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn decode_hex(value: &str) -> [u8; N] { + assert_eq!(value.len(), N * 2); + let mut output = [0u8; N]; + for (index, byte) in output.iter_mut().enumerate() { + *byte = u8::from_str_radix(&value[index * 2..index * 2 + 2], 16).unwrap(); + } + output + } + + #[test] + fn rfc8032_vector_preserves_existing_key_and_signed_message_layout() { + let seed = decode_hex::<32>( + "9d61b19deffd5a60ba844af492ec2cc4\ + 4449c5697b326919703bac031cae7f60", + ); + let public = decode_hex::<32>( + "d75a980182b10ab7d54bfed3c964073a\ + 0ee172f3daa62325af021a68f707511a", + ); + let expected_signature = decode_hex::<64>( + "e5564300c360ac729086e2cc806e828a\ + 84877f1eb8e5d974d873e06522490155\ + 5fb8821590a33bacc61e39701cf9b46b\ + d25bf5f0595bbe24655141438e7a100b", + ); + + let mut keypair = [0u8; SECRETKEYBYTES]; + keypair[..32].copy_from_slice(&seed); + keypair[32..].copy_from_slice(&public); + let secret_key = SecretKey::from_slice(&keypair).expect("RFC keypair"); + let public_key = PublicKey::from_slice(&public).expect("RFC public key"); + + let signed = sign(&[], &secret_key); + assert_eq!(signed, expected_signature); + assert_eq!(verify(&signed, &public_key), Ok(Vec::new())); + } + + #[test] + fn rejects_mismatched_stored_keypair_and_modified_signature() { + let (public_key, secret_key) = gen_keypair(); + let mut mismatched = secret_key.0.clone(); + mismatched[63] ^= 1; + assert!(SecretKey::from_slice(&mismatched).is_none()); + + let mut signed = sign(b"message", &secret_key); + signed[0] ^= 1; + assert!(verify(&signed, &public_key).is_err()); + } +} diff --git a/libs/hbb_common/src/lib.rs b/libs/hbb_common/src/lib.rs index afdb673af..ade3dc651 100644 --- a/libs/hbb_common/src/lib.rs +++ b/libs/hbb_common/src/lib.rs @@ -1,4 +1,5 @@ pub mod compress; +pub mod crypto_sign; pub mod platform; pub mod protos; pub use bytes; diff --git a/src/client.rs b/src/client.rs index a6fdc894e..79dc1c156 100644 --- a/src/client.rs +++ b/src/client.rs @@ -42,6 +42,7 @@ pub use file_trait::FileManager; #[cfg(not(feature = "flutter"))] #[cfg(not(any(target_os = "android", target_os = "ios")))] use hbb_common::tokio::sync::mpsc::UnboundedSender; +use hbb_common::crypto_sign as sign; use hbb_common::{ allow_err, anyhow::{anyhow, Context}, @@ -59,7 +60,7 @@ use hbb_common::{ rendezvous_proto::*, sha2::{Digest, Sha256}, socket_client::{connect_tcp, connect_tcp_local, ipv4_to_ipv6, new_direct_udp_for}, - sodiumoxide::{base64, crypto::sign}, + sodiumoxide::base64, timeout, tokio::{ self, diff --git a/src/common.rs b/src/common.rs index 7855c7f92..08d76b81f 100644 --- a/src/common.rs +++ b/src/common.rs @@ -10,6 +10,7 @@ use serde_json::{json, Map, Value}; #[cfg(not(target_os = "ios"))] use hbb_common::whoami; +use hbb_common::crypto_sign as sign; use hbb_common::{ allow_err, anyhow::{anyhow, Context}, @@ -26,7 +27,7 @@ use hbb_common::{ protobuf::{Enum, Message as _}, rendezvous_proto::*, socket_client, - sodiumoxide::crypto::{box_, secretbox, sign}, + sodiumoxide::crypto::{box_, secretbox}, timeout, tls::{get_cached_tls_accept_invalid_cert, get_cached_tls_type, upsert_tls_cache, TlsType}, tokio::{ diff --git a/src/custom_server.rs b/src/custom_server.rs index ede7628ed..2c00d0143 100644 --- a/src/custom_server.rs +++ b/src/custom_server.rs @@ -1,7 +1,7 @@ +use hbb_common::crypto_sign as sign; use hbb_common::{ bail, base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _}, - sodiumoxide::crypto::sign, ResultType, }; use serde_derive::{Deserialize, Serialize}; diff --git a/src/hbbs_http/sync.rs b/src/hbbs_http/sync.rs index 4df56c20a..74816f941 100644 --- a/src/hbbs_http/sync.rs +++ b/src/hbbs_http/sync.rs @@ -320,7 +320,7 @@ pub fn register_switch_grant(switch_uuid: String) { if api_server.is_empty() || crate::is_public(&api_server) { return; } - use hbb_common::sodiumoxide::crypto::{hash::sha256, sign}; + use hbb_common::{crypto_sign as sign, sodiumoxide::crypto::hash::sha256}; let switch_code = crate::encode64(sha256::hash(switch_uuid.as_bytes()).0); let switch_code_verifier = switch_code_verifier(&switch_code); let timestamp = (hbb_common::get_time() / 1000).to_string(); diff --git a/src/server.rs b/src/server.rs index 6195508de..4e446b20f 100644 --- a/src/server.rs +++ b/src/server.rs @@ -10,6 +10,7 @@ use bytes::Bytes; pub use connection::*; #[cfg(not(any(target_os = "android", target_os = "ios")))] use hbb_common::config::Config2; +use hbb_common::crypto_sign as sign; use hbb_common::tcp::{self, new_listener}; use hbb_common::{ allow_err, @@ -21,7 +22,7 @@ use hbb_common::{ protobuf::{Enum, Message as _}, rendezvous_proto::*, socket_client, - sodiumoxide::crypto::{box_, sign}, + sodiumoxide::crypto::box_, timeout, tokio, ResultType, Stream, }; use scrap::camera;