Making authenticator

pull/146/head
Chip Senkbeil 2 years ago
parent 9986af2c27
commit 6dcb943422
No known key found for this signature in database
GPG Key ID: 35EF1F8EC72A4131

@ -0,0 +1,76 @@
use super::{FramedTransport, HeapSecretKey, Reconnectable, Transport};
use async_trait::async_trait;
use std::{
collections::HashMap,
io,
ops::{Deref, DerefMut},
};
mod data;
pub use data::*;
/// Internal state for a singular transport
#[derive(Clone, Debug)]
enum State {
/// Transport is not authenticated and has not begun the process of authenticating
NotAuthenticated,
/// Transport is in the state of currently authenticating, either by issuing challenges or
/// responding with answers to challenges
Authenticating,
/// Transport has finished authenticating successfully
Authenticated {
/// Unique key that marks the transport as authenticated for use in shortcutting
/// authentication when a transport needs to reconnect. This is NOT the key used for
/// encryption and is instead meant to be shared (secretly) between client-server that are
/// aware of a previously-successful authentication.
key: HeapSecretKey,
},
}
/// Represents a stateful authenticator that is capable of performing authentication with
/// another [`Authenticator`] by communicating through a [`FramedTransport`].
///
/// ### Details
///
/// The authenticator manages a mapping of `ClientId` -> `Key` upon successful authentication which
/// can be used to verify re-authentication without needing to perform full authentication again.
/// This is particularly useful in re-connecting a `FramedTransport` post-handshake after a network
/// disruption.
#[derive(Clone, Debug)]
pub struct Authenticator {
authenticated: HashMap<String, State>,
}
impl Authenticator {
pub fn new() -> Self {
Self {
authenticated: HashMap::new(),
}
}
/// Performs authentication with the other side, moving the state to be authenticated.
pub async fn authenticate<T, const CAPACITY: usize>(
&mut self,
transport: &mut FramedTransport<T, CAPACITY>,
authentication: Authentication,
) -> io::Result<()> {
if self.is_authenticated() {
return Ok(());
}
todo!();
}
/// Clears out any tracked clients
pub fn clear(&mut self) {
self.authenticated.clear();
}
}
impl Default for Authenticator {
fn default() -> Self {
Self::new()
}
}

@ -1,5 +1,5 @@
mod any;
// mod auth;
mod auth;
mod client;
mod id;
mod listener;
@ -10,7 +10,7 @@ mod transport;
mod utils;
pub use any::*;
// pub use auth::*;
pub use auth::*;
pub use client::*;
pub use id::*;
pub use listener::*;

@ -12,9 +12,6 @@ pub use inmemory::*;
mod tcp;
pub use tcp::*;
mod stateful;
pub use stateful::*;
#[cfg(test)]
mod test;

@ -1109,18 +1109,6 @@ mod tests {
server_task.await.unwrap();
}
#[tokio::test]
async fn handshake_for_client_should_fail_if_selected_codec_choice_uses_an_unknown_compression_type(
) {
todo!();
}
#[tokio::test]
async fn handshake_for_client_should_fail_if_selected_codec_choice_uses_an_unknown_encryption_type(
) {
todo!();
}
#[tokio::test]
async fn handshake_for_client_should_fail_if_unable_to_send_key_exchange_data_to_other_side() {
todo!();

@ -1,115 +0,0 @@
use super::{FramedTransport, HeapSecretKey, Reconnectable, Transport};
use async_trait::async_trait;
use std::{
io,
ops::{Deref, DerefMut},
};
mod auth;
pub use auth::*;
/// Internal state for our transport
#[derive(Clone, Debug)]
enum State {
/// Transport is not authenticated and has not begun the process of authenticating
NotAuthenticated,
/// Transport is in the state of currently authenticating, either by issuing challenges or
/// responding with answers to challenges
Authenticating,
/// Transport has finished authenticating successfully
Authenticated {
/// Unique key that marks the transport as authenticated for use in shortcutting
/// authentication when the transport reconnects. This is NOT the key used for encryption
/// and is instead meant to be shared (secretly) between transports that are aware of a
/// previously-successful authentication.
key: HeapSecretKey,
},
}
/// Represents an stateful [`FramedTransport`] that is capable of performing authentication with
/// another [`FramedTransport`] in order to properly encrypt messages by deriving an appropriate
/// encryption codec. When authenticated, reconnecting will skip authentication unless the
/// transport on the other side declines the authenticated state.
#[derive(Clone, Debug)]
pub struct StatefulFramedTransport<T, const CAPACITY: usize> {
inner: FramedTransport<T, CAPACITY>,
state: State,
}
impl<T, const CAPACITY: usize> StatefulFramedTransport<T, CAPACITY> {
/// Creates a new stateful framed transport that is not yet authenticated
pub fn new(inner: FramedTransport<T, CAPACITY>) -> Self {
Self {
inner,
state: State::NotAuthenticated,
}
}
/// Performs authentication with the other side, moving the state to be authenticated.
pub async fn authenticate(&mut self) -> io::Result<()> {
if self.is_authenticated() {
return Ok(());
}
todo!();
}
/// Returns true if has not started the authentication process
///
/// NOTE: This will return false if in the process of authenticating, but not finished! To
/// check if not authenticated or actively authenticating, use ![`is_authenticated`].
///
/// [`is_authenticated`]: StatefulFramedTransport::is_authenticated
pub fn is_not_authenticated(&self) -> bool {
matches!(self.state, State::NotAuthenticated)
}
/// Returns true if actively authenticating
pub fn is_authenticating(&self) -> bool {
matches!(self.state, State::Authenticating)
}
/// Returns true if has authenticated successfully
pub fn is_authenticated(&self) -> bool {
matches!(self.state, State::Authenticated { .. })
}
}
impl<T, const CAPACITY: usize> Deref for StatefulFramedTransport<T, CAPACITY> {
type Target = FramedTransport<T, CAPACITY>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T, const CAPACITY: usize> DerefMut for StatefulFramedTransport<T, CAPACITY> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
#[async_trait]
impl<T, const CAPACITY: usize> Reconnectable for StatefulFramedTransport<T, CAPACITY>
where
T: Transport + Send + Sync,
{
async fn reconnect(&mut self) -> io::Result<()> {
// If authenticated, we perform a reconnect followed by re-authentication using our
// previously-acquired key to skip the need to do another authentication. Note that
// this can still change the underlying codec used by the transport if an alternative
// compression or encryption codec is picked.
if let State::Authenticated { key } = &self.state {
Reconnectable::reconnect(&mut self.inner).await?;
todo!("do handshake with key");
} else {
// If not authenticated or in the process of authenticating, we simply perform a raw
// reconnect and reset to not being authenticated
self.state = State::NotAuthenticated;
Reconnectable::reconnect(&mut self.inner).await
}
}
}
Loading…
Cancel
Save