use async_trait::async_trait;
use rand::{rngs::OsRng, RngCore};
use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::crypto::{KeyExchangeAlgorithm, SessionKeys};
use crate::messages::{
Acknowledge, ControlEnvelope, Keepalive, SessionAck, SessionComplete, SessionEstablished,
SessionInit, SessionReady,
};
pub mod client;
pub mod keepalive;
pub mod server;
pub mod transport;
#[async_trait]
pub trait HandshakeTransport {
async fn send(&mut self, msg: HandshakeMessage) -> Result<(), HandshakeError>;
async fn recv(&mut self) -> Result<HandshakeMessage, HandshakeError>;
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum HandshakeMessage {
SessionInit(SessionInit),
SessionAck(SessionAck),
SessionReady(SessionReady),
SessionComplete(SessionComplete),
SessionEstablished(SessionEstablished),
Keepalive(Keepalive),
Control(ControlEnvelope),
Ack(Acknowledge),
}
#[derive(Debug, Clone)]
pub struct HandshakeContext {
pub key_algorithm: KeyExchangeAlgorithm,
pub expected_controller: Option<String>,
pub required_firmware_rev: Option<String>,
}
impl Default for HandshakeContext {
fn default() -> Self {
Self {
key_algorithm: KeyExchangeAlgorithm::X25519,
expected_controller: None,
required_firmware_rev: None,
}
}
}
#[derive(Debug, Error)]
pub enum HandshakeError {
#[error("transport error: {0}")]
Transport(String),
#[error("protocol violation: {0}")]
Protocol(String),
#[error("authentication failed: {0}")]
Authentication(String),
#[error("unsupported capability: {0}")]
Capability(String),
}
pub fn new_nonce() -> [u8; 32] {
let mut bytes = [0u8; 32];
OsRng.fill_bytes(&mut bytes);
bytes
}
#[async_trait]
pub trait HandshakeParticipant {
async fn run<T: HandshakeTransport + Send>(
&self,
transport: &mut T,
) -> Result<HandshakeOutcome, HandshakeError>;
}
pub trait ChallengeAuthenticator {
fn sign_challenge(&self, nonce: &[u8]) -> Vec<u8>;
fn verify_challenge(&self, nonce: &[u8], signature: &[u8]) -> bool;
}
#[derive(Debug, Clone)]
pub struct HandshakeOutcome {
pub established: SessionEstablished,
pub keys: SessionKeys,
}