This commit is contained in:
2026-01-22 23:33:14 -05:00
parent ace356381a
commit bcbd6cb6c8
8 changed files with 180 additions and 77 deletions

79
Cargo.lock generated
View File

@@ -814,6 +814,27 @@ dependencies = [
"syn", "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]] [[package]]
name = "deranged" name = "deranged"
version = "0.5.5" version = "0.5.5"
@@ -1293,7 +1314,7 @@ dependencies = [
"log", "log",
"presser", "presser",
"thiserror 2.0.17", "thiserror 2.0.17",
"windows 0.58.0", "windows 0.62.2",
] ]
[[package]] [[package]]
@@ -1570,6 +1591,21 @@ dependencies = [
"wasm-bindgen", "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]] [[package]]
name = "khronos-egl" name = "khronos-egl"
version = "6.0.0" version = "6.0.0"
@@ -1599,6 +1635,15 @@ version = "0.2.178"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" 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]] [[package]]
name = "libfuzzer-sys" name = "libfuzzer-sys"
version = "0.4.10" version = "0.4.10"
@@ -2274,6 +2319,7 @@ dependencies = [
"directories-next", "directories-next",
"ed25519-dalek", "ed25519-dalek",
"iris", "iris",
"keyring",
"pollster", "pollster",
"quinn", "quinn",
"rand 0.10.0-rc.5", "rand 0.10.0-rc.5",
@@ -3007,7 +3053,7 @@ dependencies = [
"openssl-probe", "openssl-probe",
"rustls-pki-types", "rustls-pki-types",
"schannel", "schannel",
"security-framework", "security-framework 3.5.1",
] ]
[[package]] [[package]]
@@ -3035,7 +3081,7 @@ dependencies = [
"rustls-native-certs", "rustls-native-certs",
"rustls-platform-verifier-android", "rustls-platform-verifier-android",
"rustls-webpki", "rustls-webpki",
"security-framework", "security-framework 3.5.1",
"security-framework-sys", "security-framework-sys",
"webpki-root-certs", "webpki-root-certs",
"windows-sys 0.61.2", "windows-sys 0.61.2",
@@ -3128,6 +3174,19 @@ dependencies = [
"tiny-skia", "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]] [[package]]
name = "security-framework" name = "security-framework"
version = "3.5.1" version = "3.5.1"
@@ -4927,6 +4986,20 @@ name = "zeroize"
version = "1.8.2" version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" 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]] [[package]]
name = "zstd" name = "zstd"

View File

@@ -25,6 +25,7 @@ clap = { version = "4.5.53", features = ["derive"] }
scrypt = "0.11.0" scrypt = "0.11.0"
ed25519-dalek = { version = "3.0.0-pre.2", features = ["rand_core"] } ed25519-dalek = { version = "3.0.0-pre.2", features = ["rand_core"] }
rand = { version = "0.10.0-rc.5", features = ["chacha"] } rand = { version = "0.10.0-rc.5", features = ["chacha"] }
keyring = { version = "3.6.3", features = ["apple-native", "sync-secret-service", "windows-native"] }
[[bin]] [[bin]]
name = "openworm-client" name = "openworm-client"

View File

@@ -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 struct ClientData {
pub dir: DataDir, pub dir: DataDir,
@@ -18,7 +23,15 @@ impl ClientData {
self.dir.save(&self.cache); 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()
}
} }
} }

View File

@@ -2,8 +2,7 @@
use crate::{ use crate::{
data::ClientData, data::ClientData,
net::{NetHandle, NetSender}, state::{ClientState, LoggedIn},
state::{ClientState, LoggedIn, Login},
}; };
use iris::prelude::*; use iris::prelude::*;
use openworm::net::{ClientMsg, ServerMsg, install_crypto_provider}; use openworm::net::{ClientMsg, ServerMsg, install_crypto_provider};
@@ -37,7 +36,6 @@ pub struct Client {
pub type Rsc = DefaultRsc<Client>; pub type Rsc = DefaultRsc<Client>;
pub enum ClientEvent { pub enum ClientEvent {
Connect { send: NetSender },
ServerMsg(ServerMsg), ServerMsg(ServerMsg),
Err(String), Err(String),
} }
@@ -85,19 +83,6 @@ impl DefaultAppState for Client {
_render: &mut UiRenderState, _render: &mut UiRenderState,
) { ) {
match event { 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 { ClientEvent::ServerMsg(msg) => match msg {
ServerMsg::SendMsg(msg) => { ServerMsg::SendMsg(msg) => {
if let ClientState::LoggedIn(state) = &mut self.state if let ClientState::LoggedIn(state) = &mut self.state

View File

@@ -9,53 +9,37 @@ use quinn::{
use std::{ use std::{
net::{Ipv6Addr, SocketAddr, SocketAddrV6, ToSocketAddrs}, net::{Ipv6Addr, SocketAddr, SocketAddrV6, ToSocketAddrs},
sync::Arc, sync::Arc,
thread::JoinHandle,
time::Duration, time::Duration,
}; };
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::{mpsc::UnboundedSender, oneshot::Receiver};
use winit::{event_loop::EventLoopProxy, window::Window}; use winit::event_loop::EventLoopProxy;
pub const CLIENT_SOCKET: SocketAddr = pub const CLIENT_SOCKET: SocketAddr =
SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 0, 0, 0)); SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 0, 0, 0));
pub struct ConnectInfo { pub struct ConnectInfo {
pub ip: String, pub url: String,
} }
pub struct NetHandle { pub struct NetHandle {
pub send: NetSender, send: UnboundedSender<NetCtrlMsg>,
pub thread: JoinHandle<()>,
} }
#[derive(Clone)] #[derive(Clone)]
pub struct AppHandle { pub struct AppHandle {
pub proxy: EventLoopProxy<ClientEvent>, pub proxy: EventLoopProxy<ClientEvent>,
pub window: Arc<Window>,
} }
impl AppHandle { impl AppHandle {
pub fn send(&self, event: ClientEvent) { pub fn send(&self, event: ClientEvent) {
self.proxy.send_event(event).unwrap_or_else(|_| panic!()); 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<T> = Result<T, String>; type NetResult<T> = Result<T, String>;
#[derive(Clone)]
pub struct NetSender {
send: UnboundedSender<NetCtrlMsg>,
}
pub enum NetCtrlMsg { pub enum NetCtrlMsg {
Exchange(ClientMsg, Receiver<>),
Send(ClientMsg), Send(ClientMsg),
Exit, Exit,
} }
@@ -66,12 +50,6 @@ impl From<ClientMsg> for NetCtrlMsg {
} }
} }
impl NetSender {
pub fn send(&self, msg: impl Into<NetCtrlMsg>) {
let _ = self.send.send(msg.into());
}
}
impl NetHandle { impl NetHandle {
pub fn send(&self, msg: impl Into<NetCtrlMsg>) { pub fn send(&self, msg: impl Into<NetCtrlMsg>) {
self.send.send(msg.into()); self.send.send(msg.into());
@@ -79,7 +57,6 @@ impl NetHandle {
pub fn exit(self) { pub fn exit(self) {
self.send(NetCtrlMsg::Exit); 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)) Ok((endpoint, con))
} }
#[tokio::main] pub async fn connect(msg: impl MsgHandler, info: ConnectInfo) -> Result<NetHandle, String> {
async fn connect_the(handle: AppHandle, info: ConnectInfo) -> NetResult<()> {
let (send, mut ui_recv) = tokio::sync::mpsc::unbounded_channel::<NetCtrlMsg>(); let (send, mut ui_recv) = tokio::sync::mpsc::unbounded_channel::<NetCtrlMsg>();
let addr = info let addr = info
.ip .url
.to_socket_addrs() .to_socket_addrs()
.map_err(|e| e.to_string())? .map_err(|e| e.to_string())?
.next() .next()
@@ -153,38 +129,44 @@ async fn connect_the(handle: AppHandle, info: ConnectInfo) -> NetResult<()> {
let (endpoint, conn) = connection_no_cert(addr).await?; let (endpoint, conn) = connection_no_cert(addr).await?;
let conn_ = conn.clone(); let conn_ = conn.clone();
handle.send(ClientEvent::Connect { let recv = ServerRecv { msg };
send: NetSender { send },
});
let recv = ServerRecv { handle };
tokio::spawn(recv_uni(conn_, recv.into())); tokio::spawn(recv_uni(conn_, recv.into()));
tokio::spawn(async move {
while let Some(msg) = ui_recv.recv().await { while let Some(msg) = ui_recv.recv().await {
match msg { match msg {
NetCtrlMsg::Send(msg) => { NetCtrlMsg::Send(msg) => {
if send_uni(&conn, msg).await.is_err() { if send_uni(&conn, msg).await.is_err() {
println!("disconnected from server"); println!("disconnected from server");
break;
}
}
NetCtrlMsg::Exit => {
conn.close(0u32.into(), &[]);
endpoint.wait_idle().await;
break; 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<Output = ()> + Send;
}
impl<F: AsyncFn(ServerMsg) + Sync + Send + 'static> MsgHandler for F {
async fn run(&self, msg: ServerMsg) {
self(msg);
} }
Ok(())
} }
struct ServerRecv { struct ServerRecv<F: MsgHandler> {
handle: AppHandle, msg: F,
} }
impl RecvHandler<ServerMsg> for ServerRecv { impl<F: MsgHandler> RecvHandler<ServerMsg> for ServerRecv<F> {
async fn msg(&self, msg: ServerMsg) { async fn msg(&self, msg: ServerMsg) {
self.handle.send(ClientEvent::ServerMsg(msg)); self.msg.run(msg).await;
} }
} }

View File

@@ -1,3 +1,5 @@
use crate::net::{self, ConnectInfo};
use super::*; use super::*;
pub fn start(rsc: &mut Rsc) -> WeakWidget { 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); let create = Button::submit("create", rsc);
rsc.events.register(create, Submit, move |ctx, 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");
});
}); });
( (

View File

@@ -4,6 +4,7 @@ mod no_cert;
mod transfer; mod transfer;
pub use no_cert::*; pub use no_cert::*;
use rand::{TryRngCore, rngs::OsRng};
pub use transfer::*; pub use transfer::*;
pub const SERVER_NAME: &str = "openworm"; pub const SERVER_NAME: &str = "openworm";
@@ -13,8 +14,15 @@ pub const BINCODE_CONFIG: Configuration = bincode::config::standard();
pub enum ClientMsg { pub enum ClientMsg {
SendMsg(NetClientMsg), SendMsg(NetClientMsg),
RequestMsgs, RequestMsgs,
CreateAccount { username: String, password: String }, CreateAccount {
Login { username: String, password: String }, username: String,
password: String,
device: LoginKey,
},
Login {
username: String,
password: String,
},
} }
#[derive(Debug, bincode::Encode, bincode::Decode)] #[derive(Debug, bincode::Encode, bincode::Decode)]
@@ -63,3 +71,26 @@ pub fn install_crypto_provider() {
.install_default() .install_default()
.unwrap(); .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<Vec<u8>> for LoginKey {
type Error = ();
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
Ok(Self(value.try_into().map_err(|_| ())?))
}
}

View File

@@ -4,6 +4,7 @@ use std::{
}; };
use directories_next::ProjectDirs; use directories_next::ProjectDirs;
use ed25519_dalek::VerifyingKey;
use crate::net::BINCODE_CONFIG; use crate::net::BINCODE_CONFIG;
@@ -47,3 +48,5 @@ impl DataDir {
bincode::encode_into_std_write(data, &mut file, BINCODE_CONFIG).unwrap(); bincode::encode_into_std_write(data, &mut file, BINCODE_CONFIG).unwrap();
} }
} }
pub struct DevicePublicKey(VerifyingKey);