More progress

pull/146/head
Chip Senkbeil 2 years ago
parent ab849e8f9f
commit 47c8af6c1b
No known key found for this signature in database
GPG Key ID: 35EF1F8EC72A4131

@ -1,6 +1,10 @@
mod authenticated;
mod authenticator;
mod handler;
mod methods;
pub mod msg;
pub use authenticated::*;
pub use authenticator::*;
pub use handler::*;
pub use methods::*;

@ -0,0 +1,38 @@
use std::ops::{Deref, DerefMut};
/// Wrapper type around `T` that provides compile-time confirmation of being authenticated
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Authenticated<T>(T);
impl<T> Authenticated<T> {
/// Consumes authenticated wrapper and returns the inner value
pub fn into_inner(self) -> T {
self.0
}
}
impl<T> AsRef<T> for Authenticated<T> {
fn as_ref(&self) -> &T {
&self.0
}
}
impl<T> AsMut<T> for Authenticated<T> {
fn as_mut(&mut self) -> &mut T {
&mut self.0
}
}
impl<T> Deref for Authenticated<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for Authenticated<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

@ -0,0 +1,126 @@
use super::{msg::*, Authenticator};
use crate::HeapSecretKey;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::io;
/// Represents the type of authentication method to use
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum MethodType {
/// Indicates no authentication, which means this will always succeed
None,
/// Indicates that a static key is being used for authentication
StaticKey,
/// Indicates that re-authentication is being employed (using specialized key)
Reauthentication,
/// When the method is unknown (happens when other side is unaware of the method)
#[serde(other)]
Unknown,
}
/// Represents an interface to authenticate using some method
#[async_trait]
pub trait AuthenticationMethod {
async fn authenticate<A: Authenticator>(&self, authenticator: &mut A) -> io::Result<()>;
}
/// Authenticaton method for a static secret key
#[derive(Clone, Debug)]
pub struct NoneAuthenticationMethod;
impl NoneAuthenticationMethod {
#[inline]
pub fn new() -> Self {
Self
}
}
impl Default for NoneAuthenticationMethod {
#[inline]
fn default() -> Self {
Self
}
}
#[async_trait]
impl AuthenticationMethod for NoneAuthenticationMethod {
async fn authenticate<A: Authenticator>(&self, _: &mut A) -> io::Result<()> {
Ok(())
}
}
/// Authenticaton method for a static secret key
#[derive(Clone, Debug)]
pub struct StaticKeyAuthenticationMethod {
key: HeapSecretKey,
}
impl StaticKeyAuthenticationMethod {
#[inline]
pub fn new(key: impl Into<HeapSecretKey>) -> Self {
Self { key: key.into() }
}
}
#[async_trait]
impl AuthenticationMethod for StaticKeyAuthenticationMethod {
async fn authenticate<A: Authenticator>(&self, authenticator: &mut A) -> io::Result<()> {
let response = authenticator
.challenge(Challenge {
questions: vec![Question::new("key")],
options: Default::default(),
})
.await?;
if response.answers.is_empty() {
let x = Error::fatal("missing answer");
authenticator.error(x.clone()).await?;
return Err(x.into_io_permission_denied());
} else if response.answers.len() > 1 {
authenticator
.error(Error::error("more than one answer, picking first"))
.await?;
}
match response
.answers
.into_iter()
.next()
.unwrap()
.parse::<HeapSecretKey>()
{
Ok(key) if key == self.key => Ok(()),
_ => {
let x = Error::fatal("answer not a valid key");
authenticator.error(x.clone()).await?;
Err(x.into_io_permission_denied())
}
}
}
}
/// Authenticaton method for reauthentication
#[derive(Clone, Debug)]
pub struct ReauthenticationMethod {
method: StaticKeyAuthenticationMethod,
}
impl ReauthenticationMethod {
#[inline]
pub fn new(key: impl Into<HeapSecretKey>) -> Self {
Self {
method: StaticKeyAuthenticationMethod::new(key),
}
}
}
#[async_trait]
impl AuthenticationMethod for ReauthenticationMethod {
async fn authenticate<A: Authenticator>(&self, authenticator: &mut A) -> io::Result<()> {
self.method.authenticate(authenticator).await
}
}

@ -1,3 +1,4 @@
use super::MethodType;
use derive_more::{Display, Error, From};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
@ -33,27 +34,13 @@ pub enum Authentication {
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Initialization {
/// Available methods to use for authentication
pub methods: Vec<Method>,
pub methods: Vec<MethodType>,
}
/// Represents the start of authentication for some method
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct StartMethod {
pub method: Method,
}
/// Represents the type of authentication method to use by the authenticator
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Method {
/// Indicates that a static key is being used for authentication
StaticKey,
/// Indicates that re-authentication is being employed (using specialized key)
Reauthentication,
/// When the method is unknown (happens when other side is unaware of the method)
#[serde(other)]
Unknown,
pub method: MethodType,
}
/// Represents a challenge comprising a series of questions to be presented
@ -94,7 +81,7 @@ pub enum AuthenticationResponse {
/// Represents a response to initialization to specify which authentication methods to pursue
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct InitializationResponse {
pub methods: Vec<Method>,
pub methods: Vec<MethodType>,
}
/// Represents the answers to a previously-asked challenge associated with authentication
@ -123,6 +110,13 @@ pub enum VerificationKind {
Unknown,
}
impl VerificationKind {
/// Returns all variants except "unknown"
pub const fn known_variants() -> &'static [Self] {
&[Self::Host]
}
}
/// Represents a single question in a challenge associated with authentication
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Question {
@ -156,6 +150,22 @@ pub struct Error {
}
impl Error {
/// Creates a fatal error
pub fn fatal(text: impl Into<String>) -> Self {
Self {
kind: ErrorKind::Fatal,
text: text.into(),
}
}
/// Creates a non-fatal error
pub fn error(text: impl Into<String>) -> Self {
Self {
kind: ErrorKind::Error,
text: text.into(),
}
}
/// Returns true if error represents a fatal error, meaning that there is no recovery possible
/// from this error
pub fn is_fatal(&self) -> bool {

@ -42,13 +42,16 @@ where
mod tests {
use super::*;
use crate::{
auth::{AuthHandler, Authenticator, Question, VerificationKind},
auth::{
msg::{
Challenge, ChallengeResponse, Initialization, InitializationResponse, Verification,
VerificationResponse,
},
AuthHandler, Authenticator,
},
Client, ConnectionCtx, Request, ServerCtx,
};
use std::{
collections::HashMap,
net::{Ipv6Addr, SocketAddr},
};
use std::net::{Ipv6Addr, SocketAddr};
pub struct TestServer;
@ -78,16 +81,21 @@ mod tests {
#[async_trait]
impl AuthHandler for TestAuthHandler {
async fn on_challenge(
async fn on_initialization(
&mut self,
_: Vec<Question>,
_: HashMap<String, String>,
) -> io::Result<Vec<String>> {
Ok(Vec::new())
x: Initialization,
) -> io::Result<InitializationResponse> {
Ok(InitializationResponse { methods: x.methods })
}
async fn on_challenge(&mut self, _: Challenge) -> io::Result<ChallengeResponse> {
Ok(ChallengeResponse {
answers: Vec::new(),
})
}
async fn on_verify(&mut self, _: VerificationKind, _: String) -> io::Result<bool> {
Ok(true)
async fn on_verification(&mut self, _: Verification) -> io::Result<VerificationResponse> {
Ok(VerificationResponse { valid: true })
}
}

@ -45,7 +45,10 @@ mod tests {
use super::*;
use crate::{
auth::{
msg::{Challenge, ChallengeResponse, Verification, VerificationResponse},
msg::{
Challenge, ChallengeResponse, Initialization, InitializationResponse, Verification,
VerificationResponse,
},
AuthHandler, Authenticator,
},
Client, ConnectionCtx, Request, ServerCtx,
@ -80,6 +83,13 @@ mod tests {
#[async_trait]
impl AuthHandler for TestAuthHandler {
async fn on_initialization(
&mut self,
x: Initialization,
) -> io::Result<InitializationResponse> {
Ok(InitializationResponse { methods: x.methods })
}
async fn on_challenge(&mut self, _: Challenge) -> io::Result<ChallengeResponse> {
Ok(ChallengeResponse {
answers: Vec::new(),

@ -58,7 +58,13 @@ where
mod tests {
use super::*;
use crate::{
auth::{AuthHandler, AuthQuestion, AuthVerifyKind, Authenticator},
auth::{
msg::{
Challenge, ChallengeResponse, Initialization, InitializationResponse, Verification,
VerificationResponse,
},
AuthHandler, Authenticator,
},
Client, ConnectionCtx, Request, ServerCtx,
};
use std::collections::HashMap;
@ -91,16 +97,21 @@ mod tests {
#[async_trait]
impl AuthHandler for TestAuthHandler {
async fn on_challenge(
async fn on_initialization(
&mut self,
_: Vec<AuthQuestion>,
_: HashMap<String, String>,
) -> io::Result<Vec<String>> {
Ok(Vec::new())
x: Initialization,
) -> io::Result<InitializationResponse> {
Ok(InitializationResponse { methods: x.methods })
}
async fn on_verify(&mut self, _: AuthVerifyKind, _: String) -> io::Result<bool> {
Ok(true)
async fn on_challenge(&mut self, _: Challenge) -> io::Result<ChallengeResponse> {
Ok(ChallengeResponse {
answers: Vec::new(),
})
}
async fn on_verification(&mut self, _: Verification) -> io::Result<VerificationResponse> {
Ok(VerificationResponse { valid: true })
}
}

Loading…
Cancel
Save