diff --git a/Cargo.lock b/Cargo.lock index ab94444..d04e681 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -814,6 +814,27 @@ dependencies = [ "syn", ] +[[package]] +name = "dbus" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" +dependencies = [ + "libc", + "libdbus-sys", + "winapi", +] + +[[package]] +name = "dbus-secret-service" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "708b509edf7889e53d7efb0ffadd994cc6c2345ccb62f55cfd6b0682165e4fa6" +dependencies = [ + "dbus", + "zeroize", +] + [[package]] name = "deranged" version = "0.5.5" @@ -1293,7 +1314,7 @@ dependencies = [ "log", "presser", "thiserror 2.0.17", - "windows 0.58.0", + "windows 0.62.2", ] [[package]] @@ -1570,6 +1591,21 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "keyring" +version = "3.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eebcc3aff044e5944a8fbaf69eb277d11986064cba30c468730e8b9909fb551c" +dependencies = [ + "byteorder", + "dbus-secret-service", + "log", + "security-framework 2.11.1", + "security-framework 3.5.1", + "windows-sys 0.60.2", + "zeroize", +] + [[package]] name = "khronos-egl" version = "6.0.0" @@ -1599,6 +1635,15 @@ version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" +[[package]] +name = "libdbus-sys" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" +dependencies = [ + "pkg-config", +] + [[package]] name = "libfuzzer-sys" version = "0.4.10" @@ -2274,6 +2319,7 @@ dependencies = [ "directories-next", "ed25519-dalek", "iris", + "keyring", "pollster", "quinn", "rand 0.10.0-rc.5", @@ -3007,7 +3053,7 @@ dependencies = [ "openssl-probe", "rustls-pki-types", "schannel", - "security-framework", + "security-framework 3.5.1", ] [[package]] @@ -3035,7 +3081,7 @@ dependencies = [ "rustls-native-certs", "rustls-platform-verifier-android", "rustls-webpki", - "security-framework", + "security-framework 3.5.1", "security-framework-sys", "webpki-root-certs", "windows-sys 0.61.2", @@ -3128,6 +3174,19 @@ dependencies = [ "tiny-skia", ] +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.10.0", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + [[package]] name = "security-framework" version = "3.5.1" @@ -4927,6 +4986,20 @@ 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 = "zstd" diff --git a/Cargo.toml b/Cargo.toml index a4e775d..2008e4d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ clap = { version = "4.5.53", features = ["derive"] } scrypt = "0.11.0" ed25519-dalek = { version = "3.0.0-pre.2", features = ["rand_core"] } rand = { version = "0.10.0-rc.5", features = ["chacha"] } +keyring = { version = "3.6.3", features = ["apple-native", "sync-secret-service", "windows-native"] } [[bin]] name = "openworm-client" diff --git a/src/bin/client/data.rs b/src/bin/client/data.rs index 56d9349..633e714 100644 --- a/src/bin/client/data.rs +++ b/src/bin/client/data.rs @@ -1,4 +1,9 @@ -use openworm::rsc::{DataDir, DataRsc}; +use ed25519_dalek::SigningKey; +use openworm::{ + net::LoginKey, + rsc::{DataDir, DataRsc}, +}; +use rand::{TryRngCore, rngs::OsRng}; pub struct ClientData { pub dir: DataDir, @@ -18,7 +23,15 @@ impl ClientData { self.dir.save(&self.cache); } - pub fn device_key(&self) -> { + pub fn login_key(&self, user: &str) -> LoginKey { + let entry = keyring::Entry::new("openworm", user).expect("failed to open keyring entry"); + if let Ok(secret) = entry.get_secret() { + secret + .try_into() + .expect("failed to load key from secrets (invalid format)") + } else { + LoginKey::new() + } } } diff --git a/src/bin/client/main.rs b/src/bin/client/main.rs index a082f23..61e7ff8 100644 --- a/src/bin/client/main.rs +++ b/src/bin/client/main.rs @@ -2,8 +2,7 @@ use crate::{ data::ClientData, - net::{NetHandle, NetSender}, - state::{ClientState, LoggedIn, Login}, + state::{ClientState, LoggedIn}, }; use iris::prelude::*; use openworm::net::{ClientMsg, ServerMsg, install_crypto_provider}; @@ -37,7 +36,6 @@ pub struct Client { pub type Rsc = DefaultRsc; pub enum ClientEvent { - Connect { send: NetSender }, ServerMsg(ServerMsg), Err(String), } @@ -85,19 +83,6 @@ impl DefaultAppState for Client { _render: &mut UiRenderState, ) { match event { - ClientEvent::Connect { send } => { - let ClientState::Connect(connect) = self.state.take() else { - panic!("invalid state"); - }; - let th = connect.handle.unwrap(); - self.state = ClientState::Login(Login { - handle: NetHandle { - send: send.clone(), - thread: th, - }, - }); - // login_screen(self, ui).set_ptr(&self.main_ui, ui); - } ClientEvent::ServerMsg(msg) => match msg { ServerMsg::SendMsg(msg) => { if let ClientState::LoggedIn(state) = &mut self.state diff --git a/src/bin/client/net.rs b/src/bin/client/net.rs index 117b461..9dddee8 100644 --- a/src/bin/client/net.rs +++ b/src/bin/client/net.rs @@ -9,53 +9,37 @@ use quinn::{ use std::{ net::{Ipv6Addr, SocketAddr, SocketAddrV6, ToSocketAddrs}, sync::Arc, - thread::JoinHandle, time::Duration, }; -use tokio::sync::mpsc::UnboundedSender; -use winit::{event_loop::EventLoopProxy, window::Window}; +use tokio::sync::{mpsc::UnboundedSender, oneshot::Receiver}; +use winit::event_loop::EventLoopProxy; pub const CLIENT_SOCKET: SocketAddr = SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 0, 0, 0)); pub struct ConnectInfo { - pub ip: String, + pub url: String, } pub struct NetHandle { - pub send: NetSender, - pub thread: JoinHandle<()>, + send: UnboundedSender, } #[derive(Clone)] pub struct AppHandle { pub proxy: EventLoopProxy, - pub window: Arc, } impl AppHandle { pub fn send(&self, event: ClientEvent) { self.proxy.send_event(event).unwrap_or_else(|_| panic!()); - self.window.request_redraw(); } } -pub fn connect(handle: AppHandle, info: ConnectInfo) -> JoinHandle<()> { - std::thread::spawn(move || { - if let Err(msg) = connect_the(handle.clone(), info) { - handle.send(ClientEvent::Err(msg)); - } - }) -} - type NetResult = Result; -#[derive(Clone)] -pub struct NetSender { - send: UnboundedSender, -} - pub enum NetCtrlMsg { + Exchange(ClientMsg, Receiver<>), Send(ClientMsg), Exit, } @@ -66,12 +50,6 @@ impl From for NetCtrlMsg { } } -impl NetSender { - pub fn send(&self, msg: impl Into) { - let _ = self.send.send(msg.into()); - } -} - impl NetHandle { pub fn send(&self, msg: impl Into) { self.send.send(msg.into()); @@ -79,7 +57,6 @@ impl NetHandle { pub fn exit(self) { self.send(NetCtrlMsg::Exit); - let _ = self.thread.join(); } } @@ -140,12 +117,11 @@ async fn connection_no_cert(addr: SocketAddr) -> NetResult<(Endpoint, Connection Ok((endpoint, con)) } -#[tokio::main] -async fn connect_the(handle: AppHandle, info: ConnectInfo) -> NetResult<()> { +pub async fn connect(msg: impl MsgHandler, info: ConnectInfo) -> Result { let (send, mut ui_recv) = tokio::sync::mpsc::unbounded_channel::(); let addr = info - .ip + .url .to_socket_addrs() .map_err(|e| e.to_string())? .next() @@ -153,38 +129,44 @@ async fn connect_the(handle: AppHandle, info: ConnectInfo) -> NetResult<()> { let (endpoint, conn) = connection_no_cert(addr).await?; let conn_ = conn.clone(); - handle.send(ClientEvent::Connect { - send: NetSender { send }, - }); - - let recv = ServerRecv { handle }; + let recv = ServerRecv { msg }; tokio::spawn(recv_uni(conn_, recv.into())); - - while let Some(msg) = ui_recv.recv().await { - match msg { - NetCtrlMsg::Send(msg) => { - if send_uni(&conn, msg).await.is_err() { - println!("disconnected from server"); + tokio::spawn(async move { + while let Some(msg) = ui_recv.recv().await { + match msg { + NetCtrlMsg::Send(msg) => { + if send_uni(&conn, msg).await.is_err() { + println!("disconnected from server"); + break; + } + } + NetCtrlMsg::Exit => { + conn.close(0u32.into(), &[]); + endpoint.wait_idle().await; break; } } - NetCtrlMsg::Exit => { - conn.close(0u32.into(), &[]); - endpoint.wait_idle().await; - break; - } } + }); + + Ok(NetHandle { send }) +} + +pub trait MsgHandler: Sync + Send + 'static { + fn run(&self, msg: ServerMsg) -> impl Future + Send; +} +impl MsgHandler for F { + async fn run(&self, msg: ServerMsg) { + self(msg); } - - Ok(()) } -struct ServerRecv { - handle: AppHandle, +struct ServerRecv { + msg: F, } -impl RecvHandler for ServerRecv { +impl RecvHandler for ServerRecv { async fn msg(&self, msg: ServerMsg) { - self.handle.send(ClientEvent::ServerMsg(msg)); + self.msg.run(msg).await; } } diff --git a/src/bin/client/ui/connect.rs b/src/bin/client/ui/connect.rs index 0c909e9..5cbf546 100644 --- a/src/bin/client/ui/connect.rs +++ b/src/bin/client/ui/connect.rs @@ -1,3 +1,5 @@ +use crate::net::{self, ConnectInfo}; + use super::*; pub fn start(rsc: &mut Rsc) -> WeakWidget { @@ -41,7 +43,20 @@ pub fn create_account(rsc: &mut Rsc) -> WeakWidget { let create = Button::submit("create", rsc); rsc.events.register(create, Submit, move |ctx, rsc| { - ctx.state.data + let url = rsc[url].content(); + let user = rsc[username].content(); + let pwd = rsc[password].content(); + let key = ctx.state.data.login_key(&user); + rsc.spawn_task(async |ctx| { + let net = net::connect( + async |msg| { + println!("msg recv :joy:"); + }, + ConnectInfo { url }, + ) + .await + .expect("failed to connect"); + }); }); ( diff --git a/src/net/mod.rs b/src/net/mod.rs index ed02b3c..5e29c76 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -4,6 +4,7 @@ mod no_cert; mod transfer; pub use no_cert::*; +use rand::{TryRngCore, rngs::OsRng}; pub use transfer::*; pub const SERVER_NAME: &str = "openworm"; @@ -13,8 +14,15 @@ pub const BINCODE_CONFIG: Configuration = bincode::config::standard(); pub enum ClientMsg { SendMsg(NetClientMsg), RequestMsgs, - CreateAccount { username: String, password: String }, - Login { username: String, password: String }, + CreateAccount { + username: String, + password: String, + device: LoginKey, + }, + Login { + username: String, + password: String, + }, } #[derive(Debug, bincode::Encode, bincode::Decode)] @@ -63,3 +71,26 @@ pub fn install_crypto_provider() { .install_default() .unwrap(); } + +#[derive(Debug, bincode::Encode, bincode::Decode)] +pub struct LoginKey([u8; LoginKey::BYTE_LEN]); +impl LoginKey { + pub const BIT_LEN: usize = 1024; + pub const BYTE_LEN: usize = Self::BIT_LEN / 8; + + pub fn new() -> Self { + let mut key = [0u8; Self::BYTE_LEN]; + OsRng + .try_fill_bytes(&mut key) + .expect("failed to generate random key"); + Self(key) + } +} + +impl TryFrom> for LoginKey { + type Error = (); + + fn try_from(value: Vec) -> Result { + Ok(Self(value.try_into().map_err(|_| ())?)) + } +} diff --git a/src/rsc.rs b/src/rsc.rs index c0dcaa0..c5ad31d 100644 --- a/src/rsc.rs +++ b/src/rsc.rs @@ -4,6 +4,7 @@ use std::{ }; use directories_next::ProjectDirs; +use ed25519_dalek::VerifyingKey; use crate::net::BINCODE_CONFIG; @@ -47,3 +48,5 @@ impl DataDir { bincode::encode_into_std_write(data, &mut file, BINCODE_CONFIG).unwrap(); } } + +pub struct DevicePublicKey(VerifyingKey);