Track auth and service connections client side

Signed-off-by: Luca Fulchir <luca.fulchir@runesauth.com>
This commit is contained in:
Luca Fulchir 2023-05-26 15:02:21 +02:00
parent 4287540695
commit e71167224c
Signed by: luca.fulchir
GPG Key ID: 8F6440603D13A78E
7 changed files with 139 additions and 40 deletions

View File

@ -103,4 +103,8 @@ impl ServiceID {
pub const fn len() -> usize { pub const fn len() -> usize {
16 16
} }
/// read the service id as bytes
pub fn as_bytes(&self) -> &[u8; 16] {
&self.0
}
} }

View File

@ -425,7 +425,7 @@ pub struct RespData {
/// Server Connection ID /// Server Connection ID
pub id: ID, pub id: ID,
/// Service Connection ID /// Service Connection ID
pub service_id: ID, pub service_connection_id: ID,
/// Service encryption key /// Service encryption key
pub service_key: Secret, pub service_key: Secret,
} }
@ -448,7 +448,7 @@ impl RespData {
self.id.serialize(&mut out[start..end]); self.id.serialize(&mut out[start..end]);
start = end; start = end;
end = end + Self::NONCE_LEN; end = end + Self::NONCE_LEN;
self.service_id.serialize(&mut out[start..end]); self.service_connection_id.serialize(&mut out[start..end]);
start = end; start = end;
end = end + Self::NONCE_LEN; end = end + Self::NONCE_LEN;
out[start..end].copy_from_slice(self.service_key.as_ref()); out[start..end].copy_from_slice(self.service_key.as_ref());

View File

@ -36,6 +36,8 @@ pub(crate) struct HandshakeServer {
pub(crate) struct HandshakeClient { pub(crate) struct HandshakeClient {
pub id: crate::enc::asym::KeyID, pub id: crate::enc::asym::KeyID,
pub key: crate::enc::asym::PrivKey, pub key: crate::enc::asym::PrivKey,
pub service_id: crate::auth::ServiceID,
pub service_conn_id: connection::IDRecv,
pub connection: Rc<crate::connection::Connection>, pub connection: Rc<crate::connection::Connection>,
} }

View File

@ -20,10 +20,10 @@ use crate::{
}; };
/// strong typedef for receiving connection id /// strong typedef for receiving connection id
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone, PartialEq)]
pub struct IDRecv(pub ID); pub struct IDRecv(pub ID);
/// strong typedef for sending connection id /// strong typedef for sending connection id
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone, PartialEq)]
pub struct IDSend(pub ID); pub struct IDSend(pub ID);
/// Version of the fenrir protocol in use /// Version of the fenrir protocol in use
@ -86,8 +86,8 @@ impl Connection {
(hkdf.get_secret(b"to_client"), hkdf.get_secret(b"to_server")) (hkdf.get_secret(b"to_client"), hkdf.get_secret(b"to_server"))
} }
}; };
let mut cipher_recv = CipherRecv::new(cipher, secret_recv); let cipher_recv = CipherRecv::new(cipher, secret_recv);
let mut cipher_send = CipherSend::new(cipher, secret_send, rand); let cipher_send = CipherSend::new(cipher, secret_send, rand);
Self { Self {
id_recv: IDRecv(ID::Handshake), id_recv: IDRecv(ID::Handshake),
@ -111,13 +111,17 @@ pub(crate) struct ConnList {
impl ConnList { impl ConnList {
pub(crate) fn new(thread_id: ThreadTracker) -> Self { pub(crate) fn new(thread_id: ThreadTracker) -> Self {
let mut bitmap_id = ::bitmaps::Bitmap::<1024>::new(); let mut bitmap_id = ::bitmaps::Bitmap::<1024>::new();
bitmap_id.set(0, true); // ID(0) == handshake const INITIAL_CAP: usize = 128;
Self { let mut ret = Self {
thread_id, thread_id,
connections: Vec::with_capacity(128), connections: Vec::with_capacity(INITIAL_CAP),
ids_used: vec![bitmap_id], ids_used: vec![bitmap_id],
} };
ret.connections.resize_with(INITIAL_CAP, || None);
ret
} }
/// Only *Reserve* a connection,
/// without actually tracking it in self.connections
pub(crate) fn reserve_first( pub(crate) fn reserve_first(
&mut self, &mut self,
mut conn: Connection, mut conn: Connection,
@ -128,13 +132,13 @@ impl ConnList {
// initialized // initialized
// * `ID::new_u64` is really safe only with >0, but here it always is // * `ID::new_u64` is really safe only with >0, but here it always is
// ...we should probably rewrite it in better, safer rust // ...we should probably rewrite it in better, safer rust
let mut id_in_thread: u64 = 0; let mut id_in_thread: usize = 0;
let mut found = false; let mut found = false;
for (i, b) in self.ids_used.iter_mut().enumerate() { for (i, b) in self.ids_used.iter_mut().enumerate() {
match b.first_false_index() { match b.first_false_index() {
Some(idx) => { Some(idx) => {
b.set(idx, true); b.set(idx, true);
id_in_thread = ((i as u64) * 1024) + (idx as u64); id_in_thread = (i * 1024) + idx;
found = true; found = true;
break; break;
} }
@ -144,20 +148,48 @@ impl ConnList {
if !found { if !found {
let mut new_bitmap = ::bitmaps::Bitmap::<1024>::new(); let mut new_bitmap = ::bitmaps::Bitmap::<1024>::new();
new_bitmap.set(0, true); new_bitmap.set(0, true);
id_in_thread = (self.ids_used.len() as u64) * 1024; id_in_thread = self.ids_used.len() * 1024;
self.ids_used.push(new_bitmap); self.ids_used.push(new_bitmap);
} }
let actual_id = (id_in_thread * (self.thread_id.total as u64)) // make sure we have enough space in self.connections
let curr_capacity = self.connections.capacity();
if self.connections.capacity() <= id_in_thread {
// Fill with "None", assure 64 connections without reallocations
let multiple = 64 + curr_capacity - 1;
let new_capacity = multiple - (multiple % curr_capacity);
self.connections.resize_with(new_capacity, || None);
}
// calculate the actual connection ID
let actual_id = ((id_in_thread as u64) * (self.thread_id.total as u64))
+ (self.thread_id.id as u64); + (self.thread_id.id as u64);
let new_id = IDRecv(ID::new_u64(actual_id)); let new_id = IDRecv(ID::new_u64(actual_id));
conn.id_recv = new_id; conn.id_recv = new_id;
let conn = Rc::new(conn); // Return the new connection without tracking it
if (self.connections.len() as u64) < id_in_thread { Rc::new(conn)
self.connections.push(Some(conn.clone())); }
} else { /// NOTE: does NOT check if the connection has been previously reserved!
// very probably redundant pub(crate) fn track(&mut self, conn: Rc<Connection>) -> Result<(), ()> {
self.connections[id_in_thread as usize] = Some(conn.clone()); let conn_id = match conn.id_recv {
IDRecv(ID::Handshake) => {
return Err(());
}
IDRecv(ID::ID(conn_id)) => conn_id,
};
let id_in_thread: usize =
(conn_id.get() / (self.thread_id.total as u64)) as usize;
self.connections[id_in_thread] = Some(conn);
Ok(())
}
pub(crate) fn delete(&mut self, id: IDRecv) {
if let IDRecv(ID::ID(raw_id)) = id {
let id_in_thread: usize =
(raw_id.get() / (self.thread_id.total as u64)) as usize;
let vec_index = id_in_thread / 1024;
let bitmask_index = id_in_thread % 1024;
if let Some(bitmask) = self.ids_used.get_mut(vec_index) {
bitmask.set(bitmask_index, false);
self.connections[id_in_thread] = None;
}
} }
conn
} }
} }

View File

@ -115,6 +115,11 @@ impl Cipher {
} }
} }
} }
pub fn kind(&self) -> CipherKind {
match self {
Cipher::XChaCha20Poly1305(_) => CipherKind::XChaCha20Poly1305,
}
}
fn nonce_len(&self) -> HeadLen { fn nonce_len(&self) -> HeadLen {
match self { match self {
Cipher::XChaCha20Poly1305(_) => { Cipher::XChaCha20Poly1305(_) => {
@ -181,7 +186,7 @@ impl Cipher {
aad: AAD, aad: AAD,
data: &mut [u8], data: &mut [u8],
) -> Result<(), Error> { ) -> Result<(), Error> {
// FIXME: check minimum buffer size // FIXME: check minimum buffer size
match self { match self {
Cipher::XChaCha20Poly1305(cipher) => { Cipher::XChaCha20Poly1305(cipher) => {
use ::chacha20poly1305::{ use ::chacha20poly1305::{
@ -242,6 +247,10 @@ impl CipherRecv {
) -> Result<&'a [u8], Error> { ) -> Result<&'a [u8], Error> {
self.0.decrypt(aad, data) self.0.decrypt(aad, data)
} }
/// return the underlying cipher id
pub fn kind(&self) -> CipherKind {
self.0.kind()
}
} }
/// Allocate some data, with additional indexes to track /// Allocate some data, with additional indexes to track
@ -313,6 +322,10 @@ impl CipherSend {
self.cipher.encrypt(&old_nonce, aad, data)?; self.cipher.encrypt(&old_nonce, aad, data)?;
Ok(()) Ok(())
} }
/// return the underlying cipher id
pub fn kind(&self) -> CipherKind {
self.cipher.kind()
}
} }
/// XChaCha20Poly1305 cipher /// XChaCha20Poly1305 cipher

View File

@ -35,6 +35,10 @@ pub(crate) struct AuthNeededInfo {
/// Client information needed to fully establish the conenction /// Client information needed to fully establish the conenction
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct ClientConnectInfo { pub(crate) struct ClientConnectInfo {
/// The service ID that we are connecting to
pub service_id: auth::ServiceID,
/// The service ID that we are connecting to
pub service_connection_id: connection::IDRecv,
/// Parsed handshake packet /// Parsed handshake packet
pub handshake: Handshake, pub handshake: Handshake,
/// Connection /// Connection
@ -90,7 +94,7 @@ impl HandshakeTracker {
} }
} }
pub(crate) fn recv_handshake( pub(crate) fn recv_handshake(
&self, &mut self,
mut handshake: Handshake, mut handshake: Handshake,
handshake_raw: &mut [u8], handshake_raw: &mut [u8],
) -> Result<HandshakeAction, Error> { ) -> Result<HandshakeAction, Error> {
@ -175,24 +179,28 @@ impl HandshakeTracker {
})); }));
} }
DirSync::Resp(resp) => { DirSync::Resp(resp) => {
let hshake = { let hshake_idx = {
match self match self
.hshake_cli .hshake_cli
.iter() .iter()
.find(|h| h.id == resp.client_key_id) .position(|h| h.id == resp.client_key_id)
{ {
Some(h) => Some(h.clone()), Some(h) => Some(h.clone()),
None => None, None => None,
} }
}; };
if hshake.is_none() { let hshake_idx = {
::tracing::debug!( if let Some(real_idx) = hshake_idx {
"No such client key id: {:?}", real_idx
resp.client_key_id } else {
); ::tracing::debug!(
return Err(handshake::Error::UnknownKeyID.into()); "No such client key id: {:?}",
} resp.client_key_id
let hshake = hshake.unwrap(); );
return Err(handshake::Error::UnknownKeyID.into());
}
};
let hshake = &self.hshake_cli[hshake_idx];
let cipher_recv = &hshake.connection.cipher_recv; let cipher_recv = &hshake.connection.cipher_recv;
use crate::enc::sym::AAD; use crate::enc::sym::AAD;
// no aad for now // no aad for now
@ -208,8 +216,18 @@ impl HandshakeTracker {
return Err(handshake::Error::Key(e).into()); return Err(handshake::Error::Key(e).into());
} }
} }
// we can remove the handshake from the list
let hshake: HandshakeClient = {
let len = self.hshake_cli.len();
if (hshake_idx + 1) != len {
self.hshake_cli.swap(hshake_idx, len - 1);
}
self.hshake_cli.pop().unwrap()
};
return Ok(HandshakeAction::ClientConnect( return Ok(HandshakeAction::ClientConnect(
ClientConnectInfo { ClientConnectInfo {
service_id: hshake.service_id,
service_connection_id: hshake.service_conn_id,
handshake, handshake,
connection: hshake.connection, connection: hshake.connection,
}, },

View File

@ -11,7 +11,7 @@ use crate::{
socket::{UdpClient, UdpServer}, socket::{UdpClient, UdpServer},
ConnList, Connection, IDSend, Packet, ID, ConnList, Connection, IDSend, Packet, ID,
}, },
enc::sym::Secret, enc::{hkdf::HkdfSha3, sym::Secret},
inner::{HandshakeAction, HandshakeTracker, ThreadTracker}, inner::{HandshakeAction, HandshakeTracker, ThreadTracker},
}; };
use ::std::{rc::Rc, sync::Arc, vec::Vec}; use ::std::{rc::Rc, sync::Arc, vec::Vec};
@ -238,7 +238,7 @@ impl Worker {
let resp_data = dirsync::RespData { let resp_data = dirsync::RespData {
client_nonce: req_data.nonce, client_nonce: req_data.nonce,
id: auth_conn.id_recv.0, id: auth_conn.id_recv.0,
service_id: srv_conn_id, service_connection_id: srv_conn_id,
service_key: srv_secret, service_key: srv_secret,
}; };
use crate::enc::sym::AAD; use crate::enc::sym::AAD;
@ -296,10 +296,40 @@ impl Worker {
); );
return; return;
} }
// FIXME: conn tracking and arc counting {
let conn = Rc::get_mut(&mut cci.connection).unwrap(); let conn = Rc::get_mut(&mut cci.connection).unwrap();
conn.id_send = IDSend(resp_data.id); conn.id_send = IDSend(resp_data.id);
todo!(); }
// track the connection to the authentication server
if self.connections.track(cci.connection.clone()).is_err() {
self.connections.delete(cci.connection.id_recv);
}
if cci.connection.id_recv.0
== resp_data.service_connection_id
{
// the user asked a single connection
// to the authentication server, without any additional
// service. No more connections to setup
return;
}
// create and track the connection to the service
//FIXME: the Secret should be XORed with the client stored
// secret (if any)
let hkdf = HkdfSha3::new(
cci.service_id.as_bytes(),
resp_data.service_key,
);
let mut service_connection = Connection::new(
hkdf,
cci.connection.cipher_recv.kind(),
connection::Role::Client,
&self.rand,
);
service_connection.id_recv = cci.service_connection_id;
service_connection.id_send =
IDSend(resp_data.service_connection_id);
self.connections.track(service_connection.into());
return;
} }
_ => {} _ => {}
}; };
@ -326,6 +356,6 @@ impl Worker {
return; return;
} }
}; };
src_sock.send_to(&data, client.0); let _ = src_sock.send_to(&data, client.0).await;
} }
} }