rsh: implement signature verification

This commit is contained in:
Mark Poliakov 2024-11-02 19:17:32 +02:00
parent 80e6658f55
commit f0fdeb1004
14 changed files with 906 additions and 213 deletions

2
userspace/Cargo.lock generated
View File

@ -1167,10 +1167,12 @@ dependencies = [
"bytemuck", "bytemuck",
"clap", "clap",
"cross", "cross",
"ed25519-dalek",
"env_logger", "env_logger",
"libterm", "libterm",
"log", "log",
"rand 0.8.5", "rand 0.8.5",
"sha2",
"thiserror", "thiserror",
"x25519-dalek", "x25519-dalek",
] ]

View File

@ -29,6 +29,7 @@ serde = { version = "1.0.214", features = ["derive"] }
bytemuck = "1.19.0" bytemuck = "1.19.0"
thiserror = "1.0.64" thiserror = "1.0.64"
flexbuffers = "2.0.0" flexbuffers = "2.0.0"
sha2 = { version = "0.10.8" }
# Vendored/patched dependencies # Vendored/patched dependencies
rand = { git = "https://git.alnyan.me/yggdrasil/rand.git", branch = "alnyan/yggdrasil" } rand = { git = "https://git.alnyan.me/yggdrasil/rand.git", branch = "alnyan/yggdrasil" }

View File

@ -0,0 +1,3 @@
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIO6qKZCX08BZ6LQV1DJYP0ONRXWaj6qSXwSc2mtcrl9I
-----END PRIVATE KEY-----

View File

@ -15,6 +15,9 @@ clap.workspace = true
thiserror.workspace = true thiserror.workspace = true
bytemuck.workspace = true bytemuck.workspace = true
x25519-dalek.workspace = true x25519-dalek.workspace = true
ed25519-dalek = { workspace = true, features = ["rand_core", "pem"] }
sha2.workspace = true
rand = { git = "https://git.alnyan.me/yggdrasil/rand.git", branch = "alnyan/yggdrasil-rng_core-0.6.4" } rand = { git = "https://git.alnyan.me/yggdrasil/rand.git", branch = "alnyan/yggdrasil-rng_core-0.6.4" }
aes = { version = "0.8.4" } aes = { version = "0.8.4" }
log = "0.4.22" log = "0.4.22"

View File

@ -8,39 +8,53 @@ use cross::io::Poll;
use x25519_dalek::{EphemeralSecret, PublicKey, SharedSecret}; use x25519_dalek::{EphemeralSecret, PublicKey, SharedSecret};
use crate::{ use crate::{
crypt::{ClientNegotiationMessage, ServerNegotiationMessage, V1_CIPHER_AES_256_CBC, V1_KEX_X25519_DALEK}, crypt::{
socket::{MessageSocket, PacketSocket, SocketWrapper}, ciphersuite_name, sig_algo_name, signature::VerificationMethod, ClientNegotiationMessage, ServerNegotiationMessage, V1_CIPHER_AES_256_CBC, V1_KEX_X25519_DALEK
},
proto::{Decode, Decoder, Encode, Encoder},
socket::PacketSocket,
Error, Error,
}; };
use super::{ use super::{
symmetric::SymmetricCipher, ClientMessageProxy, ServerMessageProxy, V1_CIPHER_AES_256_ECB, signature::{fingerprint_sha256, SignatureKeyStore, SignatureMethod},
symmetric::SymmetricCipher,
DummyKeystore, V1_CIPHER_AES_256_ECB,
}; };
pub struct ClientConfig { pub struct ClientConfig {
pub signature_keystore: Box<dyn SignatureKeyStore>,
pub select_ciphersuite: fn(&[u8]) -> Option<u8>, pub select_ciphersuite: fn(&[u8]) -> Option<u8>,
} }
enum ClientState { enum ClientState {
PreNegotioation, PreNegotioation,
FingerprintSent(u8),
StartKexSent(u8, u8), StartKexSent(u8, u8),
ClientKeySent(u8, EphemeralSecret), ClientKeySent(u8, EphemeralSecret),
ServerKeyReceived(u8, SharedSecret), ServerKeyReceived(u8, SharedSecret),
Connected(SymmetricCipher), Connected(SymmetricCipher),
} }
struct TransportWrapper<S: PacketSocket> {
inner: S,
send_buf: [u8; 256],
}
pub struct ClientEncryptedSocket<S: PacketSocket> { pub struct ClientEncryptedSocket<S: PacketSocket> {
transport: SocketWrapper<S, ServerMessageProxy, ClientMessageProxy>, transport: TransportWrapper<S>,
state: Option<ClientState>, state: Option<ClientState>,
last_ping: Instant, last_ping: Instant,
config: ClientConfig, config: ClientConfig,
signer: Option<SignatureMethod>,
verifier: Option<VerificationMethod>,
} }
fn select_ciphersuite_default(offered: &[u8]) -> Option<u8> { fn select_ciphersuite_default(offered: &[u8]) -> Option<u8> {
// List of default accepted ciphers, ordered descending by their priority // List of default accepted ciphers, ordered descending by their priority
const ACCEPTED: &[u8] = &[ const ACCEPTED: &[u8] = &[
V1_CIPHER_AES_256_CBC, V1_CIPHER_AES_256_CBC,
V1_CIPHER_AES_256_ECB, V1_CIPHER_AES_256_ECB
]; ];
for cipher in ACCEPTED { for cipher in ACCEPTED {
@ -54,7 +68,8 @@ fn select_ciphersuite_default(offered: &[u8]) -> Option<u8> {
impl Default for ClientConfig { impl Default for ClientConfig {
fn default() -> Self { fn default() -> Self {
Self { Self {
select_ciphersuite: select_ciphersuite_default signature_keystore: Box::new(DummyKeystore),
select_ciphersuite: select_ciphersuite_default,
} }
} }
} }
@ -66,10 +81,15 @@ impl<S: PacketSocket> ClientEncryptedSocket<S> {
pub fn new_with_config(transport: S, config: ClientConfig) -> Self { pub fn new_with_config(transport: S, config: ClientConfig) -> Self {
Self { Self {
transport: SocketWrapper::new(transport), transport: TransportWrapper {
inner: transport,
send_buf: [0; 256],
},
state: None, state: None,
last_ping: Instant::now(), last_ping: Instant::now(),
config config,
signer: None,
verifier: None,
} }
} }
@ -91,21 +111,16 @@ impl<S: PacketSocket> ClientEncryptedSocket<S> {
} }
fn do_handshake_sequence(&mut self, poll: &mut Poll, timeout: Duration) -> Result<(), Error> { fn do_handshake_sequence(&mut self, poll: &mut Poll, timeout: Duration) -> Result<(), Error> {
let mut recv_buf = [0; 256]; self.transport
.send(&ClientNegotiationMessage::Hello { protocol: 1 }, None)?;
self.transport.send(
&mut recv_buf,
&ClientNegotiationMessage::Hello { protocol: 1 },
)?;
loop { loop {
let Some(fd) = poll.wait(Some(timeout))? else { let Some(fd) = poll.wait(Some(timeout))? else {
return Err(Error::Timeout); return Err(Error::Timeout);
}; };
assert_eq!(fd, self.transport.as_raw_fd()); assert_eq!(fd, self.transport.inner.as_raw_fd());
let (message, _) = self.transport.recv_from(&mut recv_buf)?; if self.update_handshake_sequence()? {
if self.update_handshake_sequence(&message)? {
break; break;
} }
} }
@ -114,17 +129,23 @@ impl<S: PacketSocket> ClientEncryptedSocket<S> {
Ok(()) Ok(())
} }
fn update_handshake_sequence( fn update_handshake_sequence(&mut self) -> Result<bool, Error> {
&mut self, let mut recv_buf = [0; 256];
message: &ServerNegotiationMessage, let message = self.transport.recv(&mut recv_buf, self.verifier.as_mut())?;
) -> Result<bool, Error> {
let mut send_buf = [0; 256];
match (message, self.state.take()) { match (message, self.state.take()) {
// PreNegotioation -> StartKexSent // PreNegotioation -> FingerprintSent
(ServerNegotiationMessage::Hello(info), Some(ClientState::PreNegotioation)) => { (ServerNegotiationMessage::Hello(info), Some(ClientState::PreNegotioation)) => {
// TODO kex algorithm selection log::debug!("Server offered ciphersuites:");
let kex_algo = V1_KEX_X25519_DALEK; for cipher in info.symmetric_ciphersuites {
let ciphersuite = match (self.config.select_ciphersuite)(info.symmetric_ciphersuites) { log::debug!("* {:?}", ciphersuite_name(*cipher));
}
log::debug!("Server offered signature algorithms:");
for algo in info.sig_algos {
log::debug!("* {:?}", sig_algo_name(*algo));
}
let ciphersuite =
match (self.config.select_ciphersuite)(info.symmetric_ciphersuites) {
// Picked a ciphersuite // Picked a ciphersuite
Some(ciphersuite) => ciphersuite, Some(ciphersuite) => ciphersuite,
// Server didn't offer anything acceptable // Server didn't offer anything acceptable
@ -132,13 +153,52 @@ impl<S: PacketSocket> ClientEncryptedSocket<S> {
return Err(Error::UnacceptableCiphersuites); return Err(Error::UnacceptableCiphersuites);
} }
}; };
log::debug!("Selected ciphersuite: {:?}", ciphersuite_name(ciphersuite));
let signer = self
.config
.signature_keystore
.signer(info.sig_algos)
.ok_or(Error::UnacceptableSignatureMethods)?;
let fingerprint = signer.fingerprint();
log::debug!("Send fingerprint: {}", fingerprint);
self.transport.send(
&ClientNegotiationMessage::SigPublicKey(
signer.algorithm(),
&signer.verifying_key_bytes(),
),
None,
)?;
self.signer = Some(signer);
self.state = Some(ClientState::FingerprintSent(ciphersuite));
Ok(false)
}
// FingerprintSent -> StartKexSent
(
ServerNegotiationMessage::SigPublicKey(their_algo, key_data),
Some(ClientState::FingerprintSent(ciphersuite)),
) => {
let algo_name = sig_algo_name(their_algo).unwrap_or("???");
let their_fingerprint = fingerprint_sha256(algo_name, key_data);
log::debug!("Server fingerprint: {}", their_fingerprint);
let verifier = self
.config
.signature_keystore
.accept_public_key(their_algo, key_data)
.ok_or(Error::PublicKeyRejected)?;
// TODO kex algorithm selection
let kex_algo = V1_KEX_X25519_DALEK;
self.transport.send( self.transport.send(
&mut send_buf,
&ClientNegotiationMessage::StartKex { &ClientNegotiationMessage::StartKex {
kex_algo, kex_algo,
ciphersuite, ciphersuite,
}, },
self.signer.as_mut(),
)?; )?;
self.verifier = Some(verifier);
self.state = Some(ClientState::StartKexSent(kex_algo, ciphersuite)); self.state = Some(ClientState::StartKexSent(kex_algo, ciphersuite));
Ok(false) Ok(false)
@ -154,8 +214,8 @@ impl<S: PacketSocket> ClientEncryptedSocket<S> {
assert!(public.as_bytes().len() < 128); assert!(public.as_bytes().len() < 128);
self.transport.send( self.transport.send(
&mut send_buf, &ClientNegotiationMessage::DHPublicKey(true, public.as_bytes()),
&ClientNegotiationMessage::PublicKey(true, public.as_bytes()), self.signer.as_mut(),
)?; )?;
self.state = Some(ClientState::ClientKeySent(ciphersuite, secret)); self.state = Some(ClientState::ClientKeySent(ciphersuite, secret));
@ -163,7 +223,7 @@ impl<S: PacketSocket> ClientEncryptedSocket<S> {
} }
// ClientKeySent -> ServerKeyReceived // ClientKeySent -> ServerKeyReceived
( (
ServerNegotiationMessage::PublicKey(true, key_data), ServerNegotiationMessage::DHPublicKey(true, key_data),
Some(ClientState::ClientKeySent(ciphersuite, secret)), Some(ClientState::ClientKeySent(ciphersuite, secret)),
) if key_data.len() == 32 => { ) if key_data.len() == 32 => {
let mut public = [0; 32]; let mut public = [0; 32];
@ -172,7 +232,7 @@ impl<S: PacketSocket> ClientEncryptedSocket<S> {
let shared = secret.diffie_hellman(&public); let shared = secret.diffie_hellman(&public);
self.transport self.transport
.send(&mut send_buf, &ClientNegotiationMessage::Agreed)?; .send(&ClientNegotiationMessage::Agreed, self.signer.as_mut())?;
self.state = Some(ClientState::ServerKeyReceived(ciphersuite, shared)); self.state = Some(ClientState::ServerKeyReceived(ciphersuite, shared));
Ok(false) Ok(false)
} }
@ -187,26 +247,42 @@ impl<S: PacketSocket> ClientEncryptedSocket<S> {
} }
Err(error) => { Err(error) => {
self.transport self.transport
.send(&mut send_buf, &ClientNegotiationMessage::Disconnect(0)) .send(
&ClientNegotiationMessage::Disconnect(0),
self.signer.as_mut(),
)
.ok(); .ok();
self.state = None; self.state = None;
Err(error) Err(error)
} }
}, },
// *** -> ***
(ServerNegotiationMessage::Ping, _) => {
self.transport.send(&ClientNegotiationMessage::Pong, None).ok();
self.last_ping = Instant::now();
Ok(false)
}
// *** -> None // *** -> None
(ServerNegotiationMessage::Kick(_reason), _) => { (ServerNegotiationMessage::Kick(_reason), _) => {
// No need to send any disconnects, server already forgot us // No need to send any disconnects, server already forgot us
self.state = None; self.reset();
Err(Error::Disconnected) Err(Error::Disconnected)
} }
(_, _) => { (_, _) => {
self.transport self.transport
.send(&mut send_buf, &ClientNegotiationMessage::Disconnect(0)) .send(&ClientNegotiationMessage::Disconnect(0), None)
.ok(); .ok();
self.state = None; self.reset();
Err(Error::Disconnected) Err(Error::Disconnected)
} }
} }
// }
}
fn reset(&mut self) {
self.signer = None;
self.verifier = None;
self.state = None;
} }
} }
@ -219,7 +295,9 @@ impl<S: PacketSocket> PacketSocket for ClientEncryptedSocket<S> {
}; };
let mut buffer = [0; 256]; let mut buffer = [0; 256];
let (message, peer) = self.transport.recv_from(&mut buffer)?; let (message, peer) = self
.transport
.recv_from(&mut buffer, self.verifier.as_mut())?;
match message { match message {
ServerNegotiationMessage::CipherText(ciphertext) => { ServerNegotiationMessage::CipherText(ciphertext) => {
let len = cipher.decrypt(ciphertext, output)?; let len = cipher.decrypt(ciphertext, output)?;
@ -227,7 +305,7 @@ impl<S: PacketSocket> PacketSocket for ClientEncryptedSocket<S> {
} }
ServerNegotiationMessage::Ping => { ServerNegotiationMessage::Ping => {
self.transport self.transport
.send(&mut buffer, &ClientNegotiationMessage::Pong)?; .send(&ClientNegotiationMessage::Pong, self.signer.as_mut())?;
self.last_ping = Instant::now(); self.last_ping = Instant::now();
// TODO this is a workaround // TODO this is a workaround
Err(Error::Ping) Err(Error::Ping)
@ -246,11 +324,10 @@ impl<S: PacketSocket> PacketSocket for ClientEncryptedSocket<S> {
}; };
let mut buffer = [0; 256]; let mut buffer = [0; 256];
let mut send_buffer = [0; 256];
let len = cipher.encrypt(data, &mut buffer)?; let len = cipher.encrypt(data, &mut buffer)?;
self.transport.send( self.transport.send(
&mut send_buffer,
&ClientNegotiationMessage::CipherText(&buffer[..len]), &ClientNegotiationMessage::CipherText(&buffer[..len]),
self.signer.as_mut(),
)?; )?;
Ok(data.len()) Ok(data.len())
} }
@ -259,9 +336,8 @@ impl<S: PacketSocket> PacketSocket for ClientEncryptedSocket<S> {
impl<S: PacketSocket> Drop for ClientEncryptedSocket<S> { impl<S: PacketSocket> Drop for ClientEncryptedSocket<S> {
fn drop(&mut self) { fn drop(&mut self) {
if self.state.is_some() { if self.state.is_some() {
let mut buffer = [0; 32];
self.transport self.transport
.send(&mut buffer, &ClientNegotiationMessage::Disconnect(0)) .send(&ClientNegotiationMessage::Disconnect(0), None)
.ok(); .ok();
} }
} }
@ -269,6 +345,64 @@ impl<S: PacketSocket> Drop for ClientEncryptedSocket<S> {
impl<S: PacketSocket> AsRawFd for ClientEncryptedSocket<S> { impl<S: PacketSocket> AsRawFd for ClientEncryptedSocket<S> {
fn as_raw_fd(&self) -> RawFd { fn as_raw_fd(&self) -> RawFd {
self.transport.as_raw_fd() self.transport.inner.as_raw_fd()
}
}
impl<S: PacketSocket> TransportWrapper<S> {
fn send(
&mut self,
message: &ClientNegotiationMessage,
signer: Option<&mut SignatureMethod>,
) -> Result<(), Error> {
let mut encoder = Encoder::new(&mut self.send_buf);
message.encode(&mut encoder)?;
let len = match (message.needs_signature(), signer) {
(false, _) => encoder.get().len(),
(true, Some(signer)) => {
let (message, signature) = encoder.split_mut();
let signature_len = signer.sign(message, signature)?;
message.len() + signature_len
}
(true, None) => unreachable!(),
};
let payload = &self.send_buf[..len];
self.inner.send(payload).map_err(Into::into)?;
Ok(())
}
fn recv_from<'de>(
&mut self,
recv_buf: &'de mut [u8],
verifier: Option<&mut VerificationMethod>,
) -> Result<(ServerNegotiationMessage<'de>, SocketAddr), Error> {
let (len, peer) = self.inner.recv_from(recv_buf).map_err(Into::into)?;
let mut decoder = Decoder::new(&recv_buf[..len]);
let message = ServerNegotiationMessage::decode(&mut decoder)?;
match (message.needs_signature(), verifier) {
(false, _) => (),
(true, Some(verifier)) => {
let (payload, signature) = decoder.split();
verifier.verify(payload, signature)?;
}
(true, None) => unimplemented!(),
};
Ok((message, peer))
}
fn recv<'de>(
&mut self,
recv_buf: &'de mut [u8],
verifier: Option<&mut VerificationMethod>,
) -> Result<ServerNegotiationMessage<'de>, Error> {
let (message, _) = self.recv_from(recv_buf, verifier)?;
Ok(message)
} }
} }

View File

@ -1,12 +1,18 @@
use std::{collections::HashSet, path::PathBuf};
use crate::proto::{Decode, DecodeError, Decoder, Encode, EncodeError, Encoder, MessageProxy}; use crate::proto::{Decode, DecodeError, Decoder, Encode, EncodeError, Encoder, MessageProxy};
pub mod client; pub mod client;
pub mod server; pub mod server;
pub mod signature;
pub mod symmetric; pub mod symmetric;
pub mod util; pub mod util;
pub use client::ClientEncryptedSocket; pub use client::ClientEncryptedSocket;
pub use server::ServerEncryptedSocket; pub use server::ServerEncryptedSocket;
use signature::{
fingerprint_sha256, SignEd25519, SignatureKeyStore, SignatureMethod, VerificationMethod,
};
pub const V1_CIPHER_AES_256_ECB: u8 = 0x10; pub const V1_CIPHER_AES_256_ECB: u8 = 0x10;
pub const V1_CIPHER_AES_256_CBC: u8 = 0x11; pub const V1_CIPHER_AES_256_CBC: u8 = 0x11;
@ -14,6 +20,9 @@ pub const V1_CIPHER_AES_256_CBC: u8 = 0x11;
// v1 supports only one DH algo // v1 supports only one DH algo
pub const V1_KEX_X25519_DALEK: u8 = 0x10; pub const V1_KEX_X25519_DALEK: u8 = 0x10;
pub const V1_SIG_ED25519: u8 = 0x10;
#[derive(Debug)]
pub enum ServerReject { pub enum ServerReject {
NoReason, NoReason,
UnsupportedKexAlgo, UnsupportedKexAlgo,
@ -22,28 +31,35 @@ pub enum ServerReject {
} }
// v1 protocol // v1 protocol
#[derive(Debug)]
pub struct ServerHello<'a> { pub struct ServerHello<'a> {
// TODO server will send its public fingerprint and advertise // TODO server will send its public fingerprint and advertise
// supported asymmetric key formats // supported asymmetric key formats
pub symmetric_ciphersuites: &'a [u8], pub symmetric_ciphersuites: &'a [u8],
pub kex_algos: &'a [u8], pub kex_algos: &'a [u8],
pub sig_algos: &'a [u8],
pub key_hash_algos: &'a [u8],
} }
#[derive(Debug)]
pub enum ClientNegotiationMessage<'a> { pub enum ClientNegotiationMessage<'a> {
Hello { protocol: u8 }, Hello { protocol: u8 },
SigPublicKey(u8, &'a [u8]),
StartKex { kex_algo: u8, ciphersuite: u8 }, StartKex { kex_algo: u8, ciphersuite: u8 },
PublicKey(bool, &'a [u8]), DHPublicKey(bool, &'a [u8]),
Agreed, Agreed,
Pong, Pong,
CipherText(&'a [u8]), CipherText(&'a [u8]),
Disconnect(u8), Disconnect(u8),
} }
#[derive(Debug)]
pub enum ServerNegotiationMessage<'a> { pub enum ServerNegotiationMessage<'a> {
Hello(ServerHello<'a>), Hello(ServerHello<'a>),
SigPublicKey(u8, &'a [u8]),
Reject(ServerReject), Reject(ServerReject),
StartKex, StartKex,
PublicKey(bool, &'a [u8]), DHPublicKey(bool, &'a [u8]),
Agreed, Agreed,
Kick(u8), Kick(u8),
Ping, Ping,
@ -53,6 +69,89 @@ pub enum ServerNegotiationMessage<'a> {
pub struct ClientMessageProxy; pub struct ClientMessageProxy;
pub struct ServerMessageProxy; pub struct ServerMessageProxy;
struct DummyKeystore;
pub struct SimpleServerKeyStore {
pub path: PathBuf,
pub accepted_keys: HashSet<String>,
}
pub struct SimpleClientKeyStore {
algo: [u8; 1],
key: Option<SignatureMethod>,
}
impl SignatureKeyStore for DummyKeystore {
fn offer_signature_algorithms(&self) -> &[u8] {
&[V1_SIG_ED25519]
}
fn signer_for_algorithm(&mut self, algorithm: u8) -> Option<SignatureMethod> {
// Just generate a one-time key
SignatureMethod::generate(algorithm).ok()
}
fn accept_public_key(&mut self, algorithm: u8, data: &[u8]) -> Option<VerificationMethod> {
// Accept any key
VerificationMethod::from_parts(algorithm, data).ok()
}
}
impl SignatureKeyStore for SimpleServerKeyStore {
fn offer_signature_algorithms(&self) -> &[u8] {
&[V1_SIG_ED25519]
}
fn signer_for_algorithm(&mut self, algorithm: u8) -> Option<SignatureMethod> {
match algorithm {
V1_SIG_ED25519 => SignEd25519::load_signing_key(self.path.join("id_ed25519"))
.map(SignatureMethod::Ed25519)
.ok(),
_ => unreachable!(),
}
}
fn accept_public_key(&mut self, algorithm: u8, data: &[u8]) -> Option<VerificationMethod> {
let algorithm_name = sig_algo_name(algorithm)?;
let fingerprint = fingerprint_sha256(algorithm_name, data);
if self.accepted_keys.contains(&fingerprint) {
VerificationMethod::from_parts(algorithm, data).ok()
} else {
None
}
}
}
impl SimpleClientKeyStore {
pub fn new(key: SignatureMethod) -> Self {
Self {
algo: match key {
SignatureMethod::Ed25519(_) => [V1_SIG_ED25519],
},
key: Some(key),
}
}
}
impl SignatureKeyStore for SimpleClientKeyStore {
fn signer_for_algorithm(&mut self, algorithm: u8) -> Option<SignatureMethod> {
if algorithm == self.algo[0] {
self.key.take()
} else {
None
}
}
fn accept_public_key(&mut self, algorithm: u8, data: &[u8]) -> Option<VerificationMethod> {
// Accept any key
VerificationMethod::from_parts(algorithm, data).ok()
}
fn offer_signature_algorithms(&self) -> &[u8] {
&self.algo
}
}
impl MessageProxy for ClientMessageProxy { impl MessageProxy for ClientMessageProxy {
type Type<'de> = ClientNegotiationMessage<'de>; type Type<'de> = ClientNegotiationMessage<'de>;
} }
@ -60,27 +159,66 @@ impl MessageProxy for ServerMessageProxy {
type Type<'de> = ServerNegotiationMessage<'de>; type Type<'de> = ServerNegotiationMessage<'de>;
} }
impl Encode for ServerHello<'_> {
fn encode(&self, buffer: &mut Encoder) -> Result<(), EncodeError> {
buffer.write_variable_bytes(self.kex_algos)?;
buffer.write_variable_bytes(self.symmetric_ciphersuites)?;
buffer.write_variable_bytes(self.sig_algos)?;
buffer.write_variable_bytes(self.key_hash_algos)?;
Ok(())
}
}
impl<'de> Decode<'de> for ServerHello<'de> {
fn decode(buffer: &mut Decoder<'de>) -> Result<Self, DecodeError> {
let kex_algos = buffer.read_variable_bytes()?;
let symmetric_ciphersuites = buffer.read_variable_bytes()?;
let sig_algos = buffer.read_variable_bytes()?;
let key_hash_algos = buffer.read_variable_bytes()?;
Ok(Self {
kex_algos,
symmetric_ciphersuites,
sig_algos,
key_hash_algos,
})
}
}
impl ClientNegotiationMessage<'_> { impl ClientNegotiationMessage<'_> {
const TAG_HELLO: u8 = 0x90; const TAG_HELLO: u8 = 0x90;
const TAG_START_KEX: u8 = 0x91; const TAG_SIG_PUBLIC_KEY: u8 = 0x91;
const TAG_PUBLIC_KEY: u8 = 0x92; const TAG_START_KEX: u8 = 0x92;
const TAG_AGREED: u8 = 0x93; const TAG_DH_PUBLIC_KEY: u8 = 0x93;
const TAG_AGREED: u8 = 0x94;
const TAG_CIPHERTEXT: u8 = 0xA0; const TAG_CIPHERTEXT: u8 = 0xA0;
const TAG_DISCONNECT: u8 = 0xA1; const TAG_DISCONNECT: u8 = 0xA1;
const TAG_PONG: u8 = 0xA2; const TAG_PONG: u8 = 0xA2;
pub fn needs_signature(&self) -> bool {
match self {
Self::Hello { .. } | Self::SigPublicKey(_, _) | Self::Pong | Self::Disconnect(_) => {
false
}
_ => true,
}
}
} }
impl Encode for ClientNegotiationMessage<'_> { impl Encode for ClientNegotiationMessage<'_> {
fn encode(&self, buffer: &mut Encoder) -> Result<(), EncodeError> { fn encode(&self, buffer: &mut Encoder) -> Result<(), EncodeError> {
match self { match self {
&Self::Hello { protocol } => buffer.write(&[Self::TAG_HELLO, protocol]), &Self::Hello { protocol } => buffer.write(&[Self::TAG_HELLO, protocol]),
&Self::SigPublicKey(algo, data) => {
buffer.write(&[Self::TAG_SIG_PUBLIC_KEY, algo])?;
buffer.write_variable_bytes(data)
}
&Self::StartKex { &Self::StartKex {
kex_algo, kex_algo,
ciphersuite, ciphersuite,
} => buffer.write(&[Self::TAG_START_KEX, kex_algo, ciphersuite]), } => buffer.write(&[Self::TAG_START_KEX, kex_algo, ciphersuite]),
Self::PublicKey(end, data) => { Self::DHPublicKey(end, data) => {
buffer.write(&[Self::TAG_PUBLIC_KEY, *end as u8])?; buffer.write(&[Self::TAG_DH_PUBLIC_KEY, *end as u8])?;
buffer.write_variable_bytes(data) buffer.write_variable_bytes(data)
} }
Self::Agreed => buffer.write(&[Self::TAG_AGREED]), Self::Agreed => buffer.write(&[Self::TAG_AGREED]),
@ -102,6 +240,11 @@ impl<'de> Decode<'de> for ClientNegotiationMessage<'de> {
let protocol = buffer.read_u8()?; let protocol = buffer.read_u8()?;
Ok(Self::Hello { protocol }) Ok(Self::Hello { protocol })
} }
Self::TAG_SIG_PUBLIC_KEY => {
let algo = buffer.read_u8()?;
let data = buffer.read_variable_bytes()?;
Ok(Self::SigPublicKey(algo, data))
}
Self::TAG_START_KEX => { Self::TAG_START_KEX => {
let kex_algo = buffer.read_u8()?; let kex_algo = buffer.read_u8()?;
let ciphersuite = buffer.read_u8()?; let ciphersuite = buffer.read_u8()?;
@ -110,10 +253,10 @@ impl<'de> Decode<'de> for ClientNegotiationMessage<'de> {
ciphersuite, ciphersuite,
}) })
} }
Self::TAG_PUBLIC_KEY => { Self::TAG_DH_PUBLIC_KEY => {
let end = buffer.read_u8()? != 0; let end = buffer.read_u8()? != 0;
let data = buffer.read_variable_bytes()?; let data = buffer.read_variable_bytes()?;
Ok(Self::PublicKey(end, data)) Ok(Self::DHPublicKey(end, data))
} }
Self::TAG_CIPHERTEXT => buffer.read_variable_bytes().map(Self::CipherText), Self::TAG_CIPHERTEXT => buffer.read_variable_bytes().map(Self::CipherText),
Self::TAG_AGREED => Ok(Self::Agreed), Self::TAG_AGREED => Ok(Self::Agreed),
@ -126,33 +269,26 @@ impl<'de> Decode<'de> for ClientNegotiationMessage<'de> {
impl ServerNegotiationMessage<'_> { impl ServerNegotiationMessage<'_> {
const TAG_HELLO: u8 = 0xB0; const TAG_HELLO: u8 = 0xB0;
const TAG_REJECT: u8 = 0xB1; const TAG_SIG_PUBLIC_KEY: u8 = 0xB1;
const TAG_START_KEX: u8 = 0xB2; const TAG_REJECT: u8 = 0xB2;
const TAG_PUBLIC_KEY: u8 = 0xB3; const TAG_START_KEX: u8 = 0xB3;
const TAG_AGREED: u8 = 0xB4; const TAG_DH_PUBLIC_KEY: u8 = 0xB4;
const TAG_AGREED: u8 = 0xB5;
const TAG_CIPHERTEXT: u8 = 0xC0; const TAG_CIPHERTEXT: u8 = 0xC0;
const TAG_PING: u8 = 0xC1; const TAG_PING: u8 = 0xC1;
const TAG_KICK: u8 = 0xC2; const TAG_KICK: u8 = 0xC2;
}
impl Encode for ServerHello<'_> { pub fn needs_signature(&self) -> bool {
fn encode(&self, buffer: &mut Encoder) -> Result<(), EncodeError> { match self {
buffer.write_variable_bytes(self.kex_algos)?; Self::Hello(_)
buffer.write_variable_bytes(self.symmetric_ciphersuites)?; | Self::Reject(_)
Ok(()) | Self::Kick(_)
| Self::Ping
| Self::SigPublicKey(_, _) => false,
_ => true,
} }
} }
impl<'de> Decode<'de> for ServerHello<'de> {
fn decode(buffer: &mut Decoder<'de>) -> Result<Self, DecodeError> {
let kex_algos = buffer.read_variable_bytes()?;
let symmetric_ciphersuites = buffer.read_variable_bytes()?;
Ok(Self {
kex_algos,
symmetric_ciphersuites,
})
}
} }
impl Encode for ServerNegotiationMessage<'_> { impl Encode for ServerNegotiationMessage<'_> {
@ -162,10 +298,14 @@ impl Encode for ServerNegotiationMessage<'_> {
buffer.write(&[Self::TAG_HELLO])?; buffer.write(&[Self::TAG_HELLO])?;
hello.encode(buffer) hello.encode(buffer)
} }
&Self::SigPublicKey(algo, key) => {
buffer.write(&[Self::TAG_SIG_PUBLIC_KEY, algo])?;
buffer.write_variable_bytes(key)
}
Self::Reject(_reason) => todo!(), Self::Reject(_reason) => todo!(),
Self::StartKex => buffer.write(&[Self::TAG_START_KEX]), Self::StartKex => buffer.write(&[Self::TAG_START_KEX]),
Self::PublicKey(end, data) => { Self::DHPublicKey(end, data) => {
buffer.write(&[Self::TAG_PUBLIC_KEY, *end as u8])?; buffer.write(&[Self::TAG_DH_PUBLIC_KEY, *end as u8])?;
buffer.write_variable_bytes(data) buffer.write_variable_bytes(data)
} }
Self::Agreed => buffer.write(&[Self::TAG_AGREED]), Self::Agreed => buffer.write(&[Self::TAG_AGREED]),
@ -184,12 +324,17 @@ impl<'de> Decode<'de> for ServerNegotiationMessage<'de> {
let tag = buffer.read_u8()?; let tag = buffer.read_u8()?;
match tag { match tag {
Self::TAG_HELLO => ServerHello::decode(buffer).map(Self::Hello), Self::TAG_HELLO => ServerHello::decode(buffer).map(Self::Hello),
Self::TAG_SIG_PUBLIC_KEY => {
let algo = buffer.read_u8()?;
let data = buffer.read_variable_bytes()?;
Ok(Self::SigPublicKey(algo, data))
}
Self::TAG_REJECT => todo!(), Self::TAG_REJECT => todo!(),
Self::TAG_START_KEX => Ok(Self::StartKex), Self::TAG_START_KEX => Ok(Self::StartKex),
Self::TAG_PUBLIC_KEY => { Self::TAG_DH_PUBLIC_KEY => {
let end = buffer.read_u8()? != 0; let end = buffer.read_u8()? != 0;
let data = buffer.read_variable_bytes()?; let data = buffer.read_variable_bytes()?;
Ok(Self::PublicKey(end, data)) Ok(Self::DHPublicKey(end, data))
} }
Self::TAG_AGREED => Ok(Self::Agreed), Self::TAG_AGREED => Ok(Self::Agreed),
Self::TAG_CIPHERTEXT => buffer.read_variable_bytes().map(Self::CipherText), Self::TAG_CIPHERTEXT => buffer.read_variable_bytes().map(Self::CipherText),
@ -205,6 +350,41 @@ pub fn ciphersuite_name(cipher: u8) -> Option<&'static str> {
match cipher { match cipher {
V1_CIPHER_AES_256_ECB => Some("aes-256-ecb"), V1_CIPHER_AES_256_ECB => Some("aes-256-ecb"),
V1_CIPHER_AES_256_CBC => Some("aes-256-cbc"), V1_CIPHER_AES_256_CBC => Some("aes-256-cbc"),
_ => None _ => None,
} }
} }
// pub fn hash_algo_name(hash: u8) -> Option<&'static str> {
// match hash {
// V1_HASH_SHA256 => Some("sha256"),
// _ => None,
// }
// }
pub fn sig_algo_name(sig: u8) -> Option<&'static str> {
match sig {
V1_SIG_ED25519 => Some("ed25519"),
_ => None,
}
}
// impl fmt::Display for PublicKeyFingerprint<'_> {
// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// let hash = match hash_algo_name(self.hash) {
// Some(name) => name,
// None => "unknown",
// };
// let sig = match sig_algo_name(self.sig) {
// Some(name) => name,
// None => "unknown",
// };
// write!(f, "{} {} {} ", sig, self.key_bits, hash)?;
// for (i, byte) in self.hash_data.iter().enumerate() {
// if i != 0 {
// write!(f, ":")?;
// }
// write!(f, "{:02x}", *byte)?;
// }
// Ok(())
// }
// }

View File

@ -9,33 +9,53 @@ use x25519_dalek::{EphemeralSecret, PublicKey};
use crate::{ use crate::{
crypt::{ crypt::{
ciphersuite_name, ServerHello, ServerNegotiationMessage, V1_CIPHER_AES_256_CBC, ciphersuite_name, sig_algo_name, ServerHello, ServerNegotiationMessage,
V1_CIPHER_AES_256_ECB, V1_CIPHER_AES_256_CBC, V1_CIPHER_AES_256_ECB,
},
socket::{
MessageSocket, MultiplexedSocket, MultiplexedSocketEvent, PacketSocket, SocketWrapper,
}, },
proto::{Decode, Decoder, Encode, Encoder},
socket::{MultiplexedSocket, MultiplexedSocketEvent, PacketSocket},
Error, Error,
}; };
use super::{ use super::{
symmetric::SymmetricCipher, ClientMessageProxy, ClientNegotiationMessage, ServerMessageProxy, signature::{SignatureKeyStore, SignatureMethod, VerificationMethod},
symmetric::SymmetricCipher,
ClientNegotiationMessage, DummyKeystore,
}; };
pub struct ServerConfig { pub struct ServerConfig {
// Returns Some(...) if a corresponding accepted key was found for the fingerprint
// None otherwise or if signature/hash algorithms are not accepted by the server
pub signature_keystore: Box<dyn SignatureKeyStore>,
pub accept_ciphersuite: fn(u8) -> bool, pub accept_ciphersuite: fn(u8) -> bool,
pub offer_ciphersuites: fn() -> &'static [u8], pub offer_ciphersuites: fn() -> &'static [u8],
} }
pub enum ServerPeerTransport { enum PeerState {
None,
PreNegotiation, PreNegotiation,
Authorized,
Negotiation(EphemeralSecret, u8), Negotiation(EphemeralSecret, u8),
Connected(SymmetricCipher, usize), Connected(SymmetricCipher),
}
struct ServerPeer {
state: PeerState,
signer: Option<SignatureMethod>,
verifier: Option<VerificationMethod>,
missed_pings: usize,
}
struct TransportWrapper<S: PacketSocket> {
inner: S,
send_buf: [u8; 256],
} }
pub struct ServerEncryptedSocket<S: PacketSocket> { pub struct ServerEncryptedSocket<S: PacketSocket> {
transport: SocketWrapper<S, ClientMessageProxy, ServerMessageProxy>, transport: TransportWrapper<S>,
peers: HashMap<SocketAddr, ServerPeerTransport>, // transport: SocketWrapper<S, ClientMessageProxy, ServerMessageProxy>,
peers: HashMap<SocketAddr, ServerPeer>,
buffer: [u8; 256], buffer: [u8; 256],
config: ServerConfig, config: ServerConfig,
} }
@ -53,6 +73,7 @@ fn offer_ciphersuites_default() -> &'static [u8] {
impl Default for ServerConfig { impl Default for ServerConfig {
fn default() -> Self { fn default() -> Self {
Self { Self {
signature_keystore: Box::new(DummyKeystore),
accept_ciphersuite: accept_ciphersuite_default, accept_ciphersuite: accept_ciphersuite_default,
offer_ciphersuites: offer_ciphersuites_default, offer_ciphersuites: offer_ciphersuites_default,
} }
@ -66,7 +87,10 @@ impl<S: PacketSocket> ServerEncryptedSocket<S> {
pub fn new_with_config(transport: S, config: ServerConfig) -> Self { pub fn new_with_config(transport: S, config: ServerConfig) -> Self {
Self { Self {
transport: SocketWrapper::new(transport), transport: TransportWrapper {
inner: transport,
send_buf: [0; 256],
},
peers: HashMap::new(), peers: HashMap::new(),
buffer: [0; 256], buffer: [0; 256],
config, config,
@ -74,32 +98,22 @@ impl<S: PacketSocket> ServerEncryptedSocket<S> {
} }
pub fn remove_client(&mut self, remote: &SocketAddr) { pub fn remove_client(&mut self, remote: &SocketAddr) {
let mut buf = [0; 32];
if self.peers.remove(remote).is_some() { if self.peers.remove(remote).is_some() {
self.transport self.transport
.send_to(remote, &mut buf, &ServerNegotiationMessage::Kick(0)) .send_to(remote, &ServerNegotiationMessage::Kick(0), None)
.ok(); .ok();
} }
} }
pub fn ping_clients(&mut self, limit: usize) -> Vec<SocketAddr> { pub fn ping_clients(&mut self, limit: usize) -> Vec<SocketAddr> {
let mut send_buf = [0; 32];
let mut removed = vec![]; let mut removed = vec![];
for (remote, state) in self.peers.iter_mut() { for (remote, peer) in self.peers.iter_mut() {
match state { self.transport.send_to(remote, &ServerNegotiationMessage::Ping, None).ok();
ServerPeerTransport::Connected(_, missed) => {
self.transport
.send_to(remote, &mut send_buf, &ServerNegotiationMessage::Ping)
.ok();
if *missed >= limit { if peer.missed_pings >= limit {
removed.push(*remote); removed.push(*remote);
} }
peer.missed_pings += 1;
*missed += 1;
}
_ => (),
}
} }
for entry in removed.iter() { for entry in removed.iter() {
@ -108,35 +122,90 @@ impl<S: PacketSocket> ServerEncryptedSocket<S> {
removed removed
} }
}
impl<S: PacketSocket> MultiplexedSocket for ServerEncryptedSocket<S> { fn recv_inner<'de>(
fn send_to(&mut self, remote: &SocketAddr, data: &[u8]) -> Result<(), Error> { &mut self,
let mut buf = [0; 256]; recv_buf: &'de mut [u8],
if let Some(ServerPeerTransport::Connected(cipher, _)) = self.peers.get_mut(remote) { ) -> Result<(ClientNegotiationMessage<'de>, SocketAddr), Error> {
let len = cipher.encrypt(data, &mut self.buffer)?; let (len, remote) = self
self.transport.send_to( .transport
remote, .inner
&mut buf, .recv_from(recv_buf)
&ServerNegotiationMessage::CipherText(&self.buffer[..len]), .map_err(Into::into)?;
) let mut decoder = Decoder::new(&recv_buf[..len]);
} else { let message = ClientNegotiationMessage::decode(&mut decoder)?;
Err(Error::NotConnected)
if message.needs_signature() {
let Some(peer) = self.peers.get_mut(&remote) else {
return Err(Error::NotConnected);
};
match peer.verifier.as_mut() {
Some(verifier) => {
let (payload, signature) = decoder.split();
if signature.is_empty() {
return Err(Error::MissingSignature);
}
verifier.verify(payload, signature)?;
}
None => {
// TODO kick the client, because it sent a signed message without a signature
todo!()
}
} }
} }
fn recv_from<'a>(&mut self, buffer: &'a mut [u8]) -> Result<MultiplexedSocketEvent<'a>, Error> { Ok((message, remote))
let (message, remote) = self.transport.recv_from(&mut self.buffer)?; }
let mut buf = [0; 256]; fn recv_from_inner<'a>(&mut self, buffer: &'a mut [u8]) -> Result<MultiplexedSocketEvent<'a>, Error> {
let mut recv_buf = [0; 256];
let (message, remote) = self.recv_inner(&mut recv_buf)?;
if let Some(mut state) = self.peers.get_mut(&remote) { if let Some(peer) = self.peers.get_mut(&remote) {
match (message, &mut state) { match (message, &mut peer.state) {
// TODO check kex params // TODO check kex params
( // PreNegotiation -> Authorized (or fail)
ClientNegotiationMessage::StartKex { ciphersuite, .. }, (ClientNegotiationMessage::SigPublicKey(sig, data), PeerState::PreNegotiation) => {
ServerPeerTransport::PreNegotiation, // TODO server might as well choose its own signature algorithm
) => { log::debug!("{remote} sent public key");
let verifier = match self.config.signature_keystore.accept_public_key(sig, data)
{
Some(verifier) => verifier,
None => {
log::warn!("{remote}: rejected public key");
self.remove_client(&remote);
return Ok(MultiplexedSocketEvent::None(remote));
}
};
let signer = match self.config.signature_keystore.signer_for_algorithm(sig) {
Some(signer) => signer,
None => {
let name = sig_algo_name(sig);
log::warn!("{remote}: no key for signature algorithm {name:?}");
self.remove_client(&remote);
return Ok(MultiplexedSocketEvent::None(remote));
}
};
// self.transport.send_to(&remote, &ServerNegotiationMessage::SigPublicKey(sig, ))?;
self.transport.send_to(
&remote,
&ServerNegotiationMessage::SigPublicKey(
sig,
&*signer.verifying_key_bytes(),
),
None,
)?;
peer.signer = Some(signer);
peer.verifier = Some(verifier);
peer.state = PeerState::Authorized;
Ok(MultiplexedSocketEvent::None(remote))
}
// Authorized -> Negotiation
(ClientNegotiationMessage::StartKex { ciphersuite, .. }, PeerState::Authorized) => {
let name = ciphersuite_name(ciphersuite); let name = ciphersuite_name(ciphersuite);
if !(self.config.accept_ciphersuite)(ciphersuite) { if !(self.config.accept_ciphersuite)(ciphersuite) {
log::warn!("Kicking {remote}: cannot accept offered ciphersuite: {name:?}",); log::warn!("Kicking {remote}: cannot accept offered ciphersuite: {name:?}",);
@ -148,59 +217,72 @@ impl<S: PacketSocket> MultiplexedSocket for ServerEncryptedSocket<S> {
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
let secret = EphemeralSecret::random_from_rng(&mut rng); let secret = EphemeralSecret::random_from_rng(&mut rng);
*state = ServerPeerTransport::Negotiation(secret, ciphersuite);
self.transport.send_to( peer.state = PeerState::Negotiation(secret, ciphersuite);
self.transport
.send_to(
&remote, &remote,
&mut buf,
&ServerNegotiationMessage::StartKex, &ServerNegotiationMessage::StartKex,
)?; peer.signer.as_mut(),
)
.ok();
Ok(MultiplexedSocketEvent::None(remote)) Ok(MultiplexedSocketEvent::None(remote))
} }
// Negotiation -> Connected
( (
ClientNegotiationMessage::PublicKey(true, data), ClientNegotiationMessage::DHPublicKey(true, data),
ServerPeerTransport::Negotiation(secret, _), PeerState::Negotiation(_, _),
) if data.len() == 32 => { ) if data.len() == 32 => {
let public = PublicKey::from(&*secret); let PeerState::Negotiation(secret, cipher) = peer.state.take() else {
unreachable!();
};
let public = PublicKey::from(&secret);
let mut remote_key = [0; 32]; let mut remote_key = [0; 32];
remote_key.copy_from_slice(data); remote_key.copy_from_slice(data);
let remote_key = PublicKey::from(remote_key); let remote_key = PublicKey::from(remote_key);
match state.negotiate(&remote_key) { let shared = secret.diffie_hellman(&remote_key);
Ok(()) => { let symmetric = match SymmetricCipher::new(cipher, shared.as_bytes()) {
// Send public key to the client Ok(symmetric) => symmetric,
self.transport.send_to(
&remote,
&mut buf,
&ServerNegotiationMessage::PublicKey(true, public.as_bytes()),
)?;
Ok(MultiplexedSocketEvent::None(remote))
}
Err(error) => { Err(error) => {
log::warn!( log::warn!("{remote}: couldn't setup symmetric cipher: {error}");
"Kicking {remote}: couldn't setup requested ciphersuite: {error}"
);
self.remove_client(&remote); self.remove_client(&remote);
return Ok(MultiplexedSocketEvent::None(remote));
}
};
self.transport
.send_to(
&remote,
&ServerNegotiationMessage::DHPublicKey(true, public.as_bytes()),
peer.signer.as_mut(),
)
.ok();
peer.state = PeerState::Connected(symmetric);
Ok(MultiplexedSocketEvent::None(remote)) Ok(MultiplexedSocketEvent::None(remote))
} }
} // Connected -> Connected
} (ClientNegotiationMessage::Agreed, PeerState::Connected(_)) => {
(ClientNegotiationMessage::Agreed, ServerPeerTransport::Connected(_, _)) => {
log::debug!("{remote}: negotiated"); log::debug!("{remote}: negotiated");
self.transport self.transport
.send_to(&remote, &mut buf, &ServerNegotiationMessage::Agreed)?; .send_to(
&remote,
&ServerNegotiationMessage::Agreed,
peer.signer.as_mut(),
)
.ok();
Ok(MultiplexedSocketEvent::None(remote)) Ok(MultiplexedSocketEvent::None(remote))
} }
( // Connected -> Connected
ClientNegotiationMessage::CipherText(data), (ClientNegotiationMessage::CipherText(data), PeerState::Connected(cipher)) => {
ServerPeerTransport::Connected(cipher, _),
) => {
let len = cipher.decrypt(data, buffer)?; let len = cipher.decrypt(data, buffer)?;
Ok(MultiplexedSocketEvent::ClientData(remote, &buffer[..len])) Ok(MultiplexedSocketEvent::ClientData(remote, &buffer[..len]))
} }
(ClientNegotiationMessage::Pong, ServerPeerTransport::Connected(_, missed)) => { // Connected -> Connected
*missed = 0; (ClientNegotiationMessage::Pong, _) => {
peer.missed_pings = 0;
Ok(MultiplexedSocketEvent::None(remote)) Ok(MultiplexedSocketEvent::None(remote))
} }
// *** -> remove
(ClientNegotiationMessage::Disconnect(_reason), _) => { (ClientNegotiationMessage::Disconnect(_reason), _) => {
log::debug!("{remote}: disconnected"); log::debug!("{remote}: disconnected");
self.peers.remove(&remote); self.peers.remove(&remote);
@ -217,44 +299,105 @@ impl<S: PacketSocket> MultiplexedSocket for ServerEncryptedSocket<S> {
let ClientNegotiationMessage::Hello { protocol: 1 } = message else { let ClientNegotiationMessage::Hello { protocol: 1 } = message else {
return Ok(MultiplexedSocketEvent::None(remote)); return Ok(MultiplexedSocketEvent::None(remote));
}; };
self.peers let peer = ServerPeer {
.insert(remote, ServerPeerTransport::PreNegotiation); signer: None,
verifier: None,
state: PeerState::PreNegotiation,
missed_pings: 0,
};
self.peers.insert(remote, peer);
// Reply with hello // Reply with hello
let symmetric_ciphersuites = (self.config.offer_ciphersuites)(); let symmetric_ciphersuites = (self.config.offer_ciphersuites)();
let sig_algos = self.config.signature_keystore.offer_signature_algorithms();
log::debug!("{remote}: offering ciphersuites:"); log::debug!("{remote}: offering ciphersuites:");
for suite in symmetric_ciphersuites { for suite in symmetric_ciphersuites {
log::debug!("* {:?}", ciphersuite_name(*suite)); log::debug!("* {:?}", ciphersuite_name(*suite));
} }
log::debug!("{remote}: offering signature algorithms:");
for sig in sig_algos {
log::debug!("* {:?}", sig_algo_name(*sig));
}
let hello = ServerHello { let hello = ServerHello {
kex_algos: &[], kex_algos: &[],
symmetric_ciphersuites, symmetric_ciphersuites,
sig_algos,
key_hash_algos: &[],
}; };
self.transport self.transport
.send_to(&remote, &mut buf, &ServerNegotiationMessage::Hello(hello))?; .send_to(&remote, &ServerNegotiationMessage::Hello(hello), None)
.ok();
Ok(MultiplexedSocketEvent::None(remote)) Ok(MultiplexedSocketEvent::None(remote))
} }
} }
} }
impl<S: PacketSocket> AsRawFd for ServerEncryptedSocket<S> { impl<S: PacketSocket> MultiplexedSocket for ServerEncryptedSocket<S> {
fn as_raw_fd(&self) -> RawFd { fn send_to(&mut self, remote: &SocketAddr, data: &[u8]) -> Result<(), Error> {
self.transport.as_raw_fd() let peer = self.peers.get_mut(remote).ok_or(Error::NotConnected)?;
let PeerState::Connected(cipher) = &mut peer.state else {
return Err(Error::NotConnected)?;
};
let len = cipher.encrypt(data, &mut self.buffer)?;
self.transport.send_to(
remote,
&ServerNegotiationMessage::CipherText(&self.buffer[..len]),
peer.signer.as_mut(),
)
}
fn recv_from<'a>(&mut self, buffer: &'a mut [u8]) -> Result<MultiplexedSocketEvent<'a>, Error> {
match self.recv_from_inner(buffer) {
Ok(event) => Ok(event),
Err(Error::Io(error)) => {
log::error!("Fatal: {error}");
Err(Error::Io(error))
}
Err(error) => {
log::warn!("{error}");
Ok(MultiplexedSocketEvent::Error(error))
}
}
} }
} }
impl ServerPeerTransport { impl<S: PacketSocket> AsRawFd for ServerEncryptedSocket<S> {
fn negotiate(&mut self, public: &PublicKey) -> Result<(), Error> { fn as_raw_fd(&self) -> RawFd {
let Self::Negotiation(secret, symmetric) = self.transport.inner.as_raw_fd()
mem::replace(self, ServerPeerTransport::PreNegotiation) }
else { }
panic!();
impl<S: PacketSocket> TransportWrapper<S> {
fn send_to(
&mut self,
remote: &SocketAddr,
message: &ServerNegotiationMessage,
signer: Option<&mut SignatureMethod>,
) -> Result<(), Error> {
let mut encoder = Encoder::new(&mut self.send_buf);
message.encode(&mut encoder)?;
let len = match (message.needs_signature(), signer) {
(false, _) => encoder.get().len(),
(true, Some(signer)) => {
let (message, signature) = encoder.split_mut();
let signature_len = signer.sign(message, signature)?;
message.len() + signature_len
}
(true, None) => unreachable!("{message:?} requires signature, but client has no signer"),
}; };
let shared = secret.diffie_hellman(public);
let cipher = SymmetricCipher::new(symmetric, shared.as_bytes())?; let payload = &self.send_buf[..len];
// let aes = Aes256::new_from_slice(shared.as_bytes()).unwrap();
*self = ServerPeerTransport::Connected(cipher, 0); self.inner.send_to(payload, remote).map_err(Into::into)?;
Ok(()) Ok(())
} }
} }
impl PeerState {
fn take(&mut self) -> Self {
mem::replace(self, Self::None)
}
}

View File

@ -0,0 +1,177 @@
use std::{borrow::Cow, fmt, io::Write, path::Path};
use ed25519_dalek::{ed25519::signature::SignerMut, pkcs8::DecodePrivateKey};
use sha2::Digest;
use crate::Error;
use super::V1_SIG_ED25519;
pub struct SignEd25519 {
signing_key: ed25519_dalek::SigningKey,
verifying_key: ed25519_dalek::VerifyingKey,
}
pub struct VerifyEd25519 {
verifying_key: ed25519_dalek::VerifyingKey,
}
pub enum SignatureMethod {
Ed25519(SignEd25519),
}
pub enum VerificationMethod {
Ed25519(VerifyEd25519),
}
pub trait SignatureKeyStore {
fn accept_public_key(&mut self, algorithm: u8, data: &[u8]) -> Option<VerificationMethod>;
fn signer_for_algorithm(&mut self, algorithm: u8) -> Option<SignatureMethod>;
fn offer_signature_algorithms(&self) -> &[u8];
fn signer(&mut self, algorithms: &[u8]) -> Option<SignatureMethod> {
for &algo in algorithms {
if let Some(signer) = self.signer_for_algorithm(algo) {
return Some(signer);
}
}
None
}
}
impl VerifyEd25519 {
pub fn from_slice(key: &[u8]) -> Result<Self, Error> {
if key.len() != ed25519_dalek::PUBLIC_KEY_LENGTH {
return Err(Error::InvalidKey);
}
let mut bytes = [0; 32];
bytes.copy_from_slice(key);
let verifying_key =
ed25519_dalek::VerifyingKey::from_bytes(&bytes).map_err(|_| Error::InvalidKey)?;
Ok(Self { verifying_key })
}
pub fn verify(&mut self, message: &[u8], signature: &[u8]) -> Result<(), Error> {
if signature.len() != ed25519_dalek::SIGNATURE_LENGTH {
return Err(Error::InvalidSignatureSize(signature.len()));
}
let signature = ed25519_dalek::Signature::from_slice(signature).unwrap();
self.verifying_key
.verify_strict(message, &signature)
.map_err(|_| Error::InvalidSignature)
}
pub fn fingerprint(&self) -> String {
fingerprint_sha256("ed25519", self.verifying_key.as_bytes())
}
}
impl SignEd25519 {
pub fn generate() -> Result<Self, Error> {
let mut rng = rand::thread_rng();
let signing_key = ed25519_dalek::SigningKey::generate(&mut rng);
let verifying_key = signing_key.verifying_key();
Ok(Self {
signing_key,
verifying_key,
})
}
pub fn load_signing_key<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
let signing_key = ed25519_dalek::SigningKey::read_pkcs8_pem_file(path).unwrap();
let verifying_key = signing_key.verifying_key();
Ok(Self {
signing_key,
verifying_key,
})
}
pub fn sign(&mut self, message: &[u8], buffer: &mut [u8]) -> Result<usize, Error> {
if buffer.len() < ed25519_dalek::SIGNATURE_LENGTH {
return Err(Error::CannotFitSignature(
buffer.len(),
ed25519_dalek::SIGNATURE_LENGTH,
));
}
let signature = self.signing_key.sign(message);
buffer[..ed25519_dalek::SIGNATURE_LENGTH].copy_from_slice(&signature.to_bytes());
Ok(ed25519_dalek::SIGNATURE_LENGTH)
}
pub fn verifying_key_bytes(&self) -> Cow<[u8]> {
Cow::Borrowed(self.verifying_key.as_bytes())
}
pub fn fingerprint(&self) -> String {
fingerprint_sha256("ed25519", self.verifying_key.as_bytes())
}
}
impl VerificationMethod {
pub fn from_parts(algorithm: u8, data: &[u8]) -> Result<Self, Error> {
match algorithm {
V1_SIG_ED25519 => VerifyEd25519::from_slice(data).map(Self::Ed25519),
_ => todo!(),
}
}
pub fn verify(&mut self, message: &[u8], signature: &[u8]) -> Result<(), Error> {
match self {
Self::Ed25519(ed25519) => ed25519.verify(message, signature),
}
}
pub fn fingerprint(&self) -> String {
match self {
Self::Ed25519(ed25519) => ed25519.fingerprint(),
}
}
}
impl SignatureMethod {
pub fn generate(algorithm: u8) -> Result<Self, Error> {
match algorithm {
V1_SIG_ED25519 => SignEd25519::generate().map(Self::Ed25519),
_ => todo!(),
}
}
pub fn sign(&mut self, message: &[u8], signature: &mut [u8]) -> Result<usize, Error> {
match self {
Self::Ed25519(ed25519) => ed25519.sign(message, signature),
}
}
pub fn verifying_key_bytes(&self) -> Cow<[u8]> {
match self {
Self::Ed25519(ed25519) => ed25519.verifying_key_bytes(),
}
}
pub fn algorithm(&self) -> u8 {
match self {
Self::Ed25519(_) => V1_SIG_ED25519,
}
}
pub fn fingerprint(&self) -> String {
match self {
Self::Ed25519(ed25519) => ed25519.fingerprint(),
}
}
}
pub fn fingerprint_sha256(key_algo: impl fmt::Display, key_bytes: &[u8]) -> String {
let mut digest = sha2::Sha256::new();
digest.write_all(key_bytes).unwrap();
let hash = digest.finalize();
let mut result = format!("{} sha256 ", key_algo);
for (i, &byte) in hash.iter().enumerate() {
if i != 0 {
result.push(':');
}
result.push_str(&format!("{byte:02x}"));
}
result
}

View File

@ -32,12 +32,26 @@ pub enum Error {
Disconnected, Disconnected,
#[error("Message too large: buffer size {0}, message size {1}")] #[error("Message too large: buffer size {0}, message size {1}")]
MessageTooLarge(usize, usize), MessageTooLarge(usize, usize),
#[error("Cannot sign message: buffer size {0}, signature size {1}")]
CannotFitSignature(usize, usize),
#[error("Malformed ciphertext")] #[error("Malformed ciphertext")]
InvalidCiphertext, InvalidCiphertext,
#[error("Message signature mismatch")]
InvalidSignature,
#[error("Invalid signature size ({0})")]
InvalidSignatureSize(usize),
#[error("Malformed encryption key")] #[error("Malformed encryption key")]
InvalidKey, InvalidKey,
#[error("Communication timed out")] #[error("Communication timed out")]
Timeout, Timeout,
#[error("Cannot accept any of the offered ciphersuites")] #[error("Cannot accept any of the offered ciphersuites")]
UnacceptableCiphersuites, UnacceptableCiphersuites,
#[error("Cannot accept any of the offered signature methods")]
UnacceptableSignatureMethods,
#[error("No verifying key for remote signature method")]
NoVerifyingKey,
#[error("Message is not signed")]
MissingSignature,
#[error("Rejected remote public key")]
PublicKeyRejected,
} }

View File

@ -1,18 +1,14 @@
#![cfg_attr(target_os = "yggdrasil", feature(yggdrasil_os))] #![cfg_attr(target_os = "yggdrasil", feature(yggdrasil_os))]
use std::{ use std::{
io::{self, stdin, stdout, Read, Stdin, Stdout, Write as IoWrite}, io::{self, stdin, stdout, Read, Stdin, Stdout, Write as IoWrite}, net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, UdpSocket}, os::fd::AsRawFd, path::PathBuf, process::ExitCode, time::{Duration, Instant}
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, UdpSocket},
os::fd::AsRawFd,
process::ExitCode,
time::{Duration, Instant},
}; };
use clap::Parser; use clap::Parser;
use cross::io::Poll; use cross::io::Poll;
use libterm::{RawMode, RawTerminal}; use libterm::{RawMode, RawTerminal};
use rsh::{ use rsh::{
crypt::ClientEncryptedSocket, proto::{ClientMessage, ServerMessage, TerminalInfo}, socket::{MessageSocket, PacketSocket}, ClientSocket crypt::{client::ClientConfig, signature::{SignEd25519, SignatureMethod}, ClientEncryptedSocket, SimpleClientKeyStore}, proto::{ClientMessage, ServerMessage, TerminalInfo}, socket::{MessageSocket, PacketSocket}, ClientSocket
}; };
pub const PING_TIMEOUT: Duration = Duration::from_secs(3); pub const PING_TIMEOUT: Duration = Duration::from_secs(3);
@ -33,6 +29,8 @@ pub enum Error {
#[derive(Debug, Parser)] #[derive(Debug, Parser)]
struct Args { struct Args {
#[clap(short, long)]
key: PathBuf,
remote: IpAddr, remote: IpAddr,
} }
@ -52,7 +50,7 @@ pub enum Event<'b> {
} }
impl Client { impl Client {
pub fn connect(remote: SocketAddr) -> Result<Self, Error> { pub fn connect(remote: SocketAddr, crypto_config: ClientConfig) -> Result<Self, Error> {
let mut poll = Poll::new()?; let mut poll = Poll::new()?;
let stdin = stdin(); let stdin = stdin();
let stdout = stdout(); let stdout = stdout();
@ -66,7 +64,7 @@ impl Client {
let socket = UdpSocket::bind(local)?; let socket = UdpSocket::bind(local)?;
socket.connect(remote)?; socket.connect(remote)?;
let mut socket = ClientEncryptedSocket::new(socket); let mut socket = ClientEncryptedSocket::new_with_config(socket, crypto_config);
poll.add(&socket)?; poll.add(&socket)?;
@ -225,8 +223,14 @@ fn terminal_info(stdout: &Stdout) -> Result<TerminalInfo, Error> {
} }
fn run(args: Args) -> Result<(), Error> { fn run(args: Args) -> Result<(), Error> {
let remote = SocketAddr::new(args.remote, 7777); let remote = SocketAddr::new(args.remote, 77);
let reason = Client::connect(remote)?.run()?; let ed25519 = SignEd25519::load_signing_key(args.key).unwrap();
let key = SignatureMethod::Ed25519(ed25519);
let config = ClientConfig {
signature_keystore: Box::new(SimpleClientKeyStore::new(key)),
..Default::default()
};
let reason = Client::connect(remote, config)?.run()?;
if !reason.is_empty() { if !reason.is_empty() {
eprintln!("\nDisconnected: {reason}"); eprintln!("\nDisconnected: {reason}");
} }

View File

@ -71,7 +71,7 @@ impl<'a> Encoder<'a> {
} }
pub fn write_variable_bytes(&mut self, bytes: &[u8]) -> Result<(), EncodeError> { pub fn write_variable_bytes(&mut self, bytes: &[u8]) -> Result<(), EncodeError> {
let len: u32 = bytes let len: u16 = bytes
.len() .len()
.try_into() .try_into()
.map_err(|_| EncodeError::ValueTooLong)?; .map_err(|_| EncodeError::ValueTooLong)?;
@ -95,6 +95,10 @@ impl<'a> Encoder<'a> {
pub fn get(&self) -> &[u8] { pub fn get(&self) -> &[u8] {
&self.buffer[..self.pos] &self.buffer[..self.pos]
} }
pub fn split_mut(&mut self) -> (&mut [u8], &mut [u8]) {
self.buffer.split_at_mut(self.pos)
}
} }
impl<'a> Decoder<'a> { impl<'a> Decoder<'a> {
@ -111,6 +115,11 @@ impl<'a> Decoder<'a> {
Ok(byte) Ok(byte)
} }
pub fn read_le_u16(&mut self) -> Result<u16, DecodeError> {
let bytes = self.read_bytes(size_of::<u16>())?;
Ok(u16::from_le_bytes([bytes[0], bytes[1]]))
}
pub fn read_le_u32(&mut self) -> Result<u32, DecodeError> { pub fn read_le_u32(&mut self) -> Result<u32, DecodeError> {
let bytes = self.read_bytes(size_of::<u32>())?; let bytes = self.read_bytes(size_of::<u32>())?;
Ok(u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]])) Ok(u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
@ -126,7 +135,7 @@ impl<'a> Decoder<'a> {
} }
pub fn read_variable_bytes(&mut self) -> Result<&'a [u8], DecodeError> { pub fn read_variable_bytes(&mut self) -> Result<&'a [u8], DecodeError> {
let len = self.read_le_u32()?; let len = self.read_le_u16()?;
self.read_bytes(len as usize) self.read_bytes(len as usize)
} }
@ -134,6 +143,10 @@ impl<'a> Decoder<'a> {
let slice = self.read_variable_bytes()?; let slice = self.read_variable_bytes()?;
core::str::from_utf8(slice).map_err(DecodeError::InvalidString) core::str::from_utf8(slice).map_err(DecodeError::InvalidString)
} }
pub fn split(&self) -> (&'a [u8], &'a [u8]) {
self.buffer.split_at(self.pos)
}
} }
impl MessageProxy for ClientMessageProxy { impl MessageProxy for ClientMessageProxy {

View File

@ -1,19 +1,13 @@
#![cfg_attr(target_os = "yggdrasil", feature(yggdrasil_os, rustc_private))] #![cfg_attr(target_os = "yggdrasil", feature(yggdrasil_os, rustc_private))]
#![feature(if_let_guard)] #![feature(if_let_guard)]
use std::{ use std::{
collections::HashMap, collections::{HashMap, HashSet}, fs::File, io::{Read, Write}, net::{SocketAddr, UdpSocket}, os::fd::{AsRawFd, FromRawFd, RawFd}, path::PathBuf, process::{Child, Command, ExitCode, Stdio}, str::FromStr, time::Duration
fs::File,
io::{Read, Write},
net::{SocketAddr, UdpSocket},
os::fd::{AsRawFd, FromRawFd, RawFd},
process::{Child, Command, ExitCode, Stdio},
str::FromStr,
time::Duration,
}; };
use clap::Parser;
use cross::io::{Poll, TimerFd}; use cross::io::{Poll, TimerFd};
use rsh::{ use rsh::{
crypt::ServerEncryptedSocket, crypt::{server::ServerConfig, ServerEncryptedSocket, SimpleServerKeyStore},
proto::{ClientMessage, Decode, Decoder, ServerMessage, TerminalInfo}, proto::{ClientMessage, Decode, Decoder, ServerMessage, TerminalInfo},
socket::{MultiplexedSocket, MultiplexedSocketEvent}, socket::{MultiplexedSocket, MultiplexedSocketEvent},
Error, Error,
@ -21,6 +15,14 @@ use rsh::{
pub const PING_INTERVAL: Duration = Duration::from_millis(500); pub const PING_INTERVAL: Duration = Duration::from_millis(500);
#[derive(Debug, clap::Parser)]
struct Args {
#[clap(short = 'P', long, help = "rsh listen port", default_value_t = 77)]
port: u16,
#[clap(short = 'S', long, help = "where rsh will load private keys from", default_value = "/etc/rsh")]
keystore: PathBuf
}
pub struct Session { pub struct Session {
pty_master: File, pty_master: File,
remote: SocketAddr, remote: SocketAddr,
@ -95,10 +97,11 @@ impl Session {
} }
impl Server { impl Server {
pub fn new(listen_addr: SocketAddr) -> Result<Self, Error> { pub fn new(listen_addr: SocketAddr, crypto_config: ServerConfig) -> Result<Self, Error> {
let mut poll = Poll::new()?; let mut poll = Poll::new()?;
let timer = TimerFd::new()?; let timer = TimerFd::new()?;
let socket = UdpSocket::bind(listen_addr).map(ServerEncryptedSocket::new)?; let socket = UdpSocket::bind(listen_addr)?;
let socket = ServerEncryptedSocket::new_with_config(socket, crypto_config);
poll.add(&socket)?; poll.add(&socket)?;
poll.add(&timer)?; poll.add(&timer)?;
Ok(Self { Ok(Self {
@ -132,6 +135,7 @@ impl Server {
let message = ClientMessage::decode(&mut decoder); let message = ClientMessage::decode(&mut decoder);
(message, peer) (message, peer)
} }
MultiplexedSocketEvent::Error(_) => return Ok(None),
}; };
let message = match message { let message = match message {
@ -187,7 +191,6 @@ impl Server {
let session = self.pty_to_session.get_mut(&fd).unwrap(); let session = self.pty_to_session.get_mut(&fd).unwrap();
if let Err(error) = session.pty_master.write(&data) { if let Err(error) = session.pty_master.write(&data) {
eprintln!("PTY write error: {error}"); eprintln!("PTY write error: {error}");
self.remove_session_by_fd(fd)?;
self.socket self.socket
.send_message_to( .send_message_to(
&remote, &remote,
@ -195,6 +198,7 @@ impl Server {
&ServerMessage::Bye("PTY error"), &ServerMessage::Bye("PTY error"),
) )
.ok(); .ok();
self.remove_session_by_fd(fd)?;
} }
} }
Event::ClientBye(remote, reason) => { Event::ClientBye(remote, reason) => {
@ -219,6 +223,7 @@ impl Server {
&ServerMessage::Bye("PTY open error"), &ServerMessage::Bye("PTY open error"),
) )
.ok(); .ok();
self.socket.remove_client(&remote);
} }
} }
} }
@ -230,7 +235,6 @@ impl Server {
} }
PtyEvent::Err(error) => { PtyEvent::Err(error) => {
eprintln!("PTY read error: {error}"); eprintln!("PTY read error: {error}");
self.remove_session_by_fd(fd)?;
self.socket self.socket
.send_message_to( .send_message_to(
&remote, &remote,
@ -238,13 +242,14 @@ impl Server {
&ServerMessage::Bye("PTY error"), &ServerMessage::Bye("PTY error"),
) )
.ok(); .ok();
self.remove_session_by_fd(fd)?;
} }
PtyEvent::Closed => { PtyEvent::Closed => {
println!("End of PTY for {remote}"); println!("End of PTY for {remote}");
self.remove_session_by_fd(fd)?;
self.socket self.socket
.send_message_to(&remote, &mut send_buf, &ServerMessage::Bye("")) .send_message_to(&remote, &mut send_buf, &ServerMessage::Bye(""))
.ok(); .ok();
self.remove_session_by_fd(fd)?;
} }
}, },
Event::Tick => { Event::Tick => {
@ -259,6 +264,7 @@ impl Server {
fn update_client_timeouts(&mut self) -> Result<(), Error> { fn update_client_timeouts(&mut self) -> Result<(), Error> {
let removed = self.socket.ping_clients(8); let removed = self.socket.ping_clients(8);
for entry in removed { for entry in removed {
log::debug!("Client timed out: {entry}");
self.remove_session_by_remote(entry).ok(); self.remove_session_by_remote(entry).ok();
} }
Ok(()) Ok(())
@ -294,8 +300,19 @@ impl Server {
} }
} }
fn run() -> Result<(), Error> { fn run(args: Args) -> Result<(), Error> {
let server = Server::new(SocketAddr::from_str("0.0.0.0:7777").unwrap())?; let keystore = Box::new(SimpleServerKeyStore {
path: args.keystore,
accepted_keys: HashSet::from_iter([
"ed25519 sha256 63:f4:df:41:c3:ec:a7:7c:ea:f1:65:22:91:8b:14:60:8f:ec:cd:19:90:2e:12:33:66:e3:33:24:96:3d:63:c3".into()
]),
});
let server_config = ServerConfig {
signature_keystore: keystore,
..Default::default()
};
let listen_addr = SocketAddr::from_str(&format!("0.0.0.0:{}", args.port)).unwrap();
let server = Server::new(listen_addr, server_config)?;
server.run() server.run()
} }
@ -304,7 +321,8 @@ fn main() -> ExitCode {
.filter_level(log::LevelFilter::Debug) .filter_level(log::LevelFilter::Debug)
.format_timestamp(None) .format_timestamp(None)
.init(); .init();
if let Err(error) = run() { let args = Args::parse();
if let Err(error) = run(args) {
eprintln!("Finished with error: {error}"); eprintln!("Finished with error: {error}");
ExitCode::FAILURE ExitCode::FAILURE
} else { } else {

View File

@ -44,6 +44,7 @@ pub enum MultiplexedSocketEvent<'a> {
ClientData(SocketAddr, &'a [u8]), ClientData(SocketAddr, &'a [u8]),
ClientDisconnected(SocketAddr), ClientDisconnected(SocketAddr),
None(SocketAddr), None(SocketAddr),
Error(Error)
} }
pub trait MultiplexedSocket: AsRawFd { pub trait MultiplexedSocket: AsRawFd {

View File

@ -15,10 +15,10 @@ thiserror.workspace = true
clap.workspace = true clap.workspace = true
serde.workspace = true serde.workspace = true
serde_json.workspace = true serde_json.workspace = true
sha2.workspace = true
# TODO own impl # TODO own impl
humansize = { version = "2.1.3", features = ["impl_style"] } humansize = { version = "2.1.3", features = ["impl_style"] }
sha2 = { version = "0.10.8" }
init = { path = "../init" } init = { path = "../init" }