alpine-protocol-rs 2.0.21

Authenticated Lighting Protocol (alpine): secure control-plane + streaming guard for lighting data.
Documentation
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;

/// Transport abstraction used during the ALNP handshake.
#[async_trait]
pub trait HandshakeTransport {
    async fn send(&mut self, msg: HandshakeMessage) -> Result<(), HandshakeError>;
    async fn recv(&mut self) -> Result<HandshakeMessage, HandshakeError>;
}

/// Minimal message envelope for the handshake pipeline.
#[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),
}

/// Context shared between handshake participants.
#[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),
}

/// Generates a cryptographic nonce for challenge/response.
pub fn new_nonce() -> [u8; 32] {
    let mut bytes = [0u8; 32];
    OsRng.fill_bytes(&mut bytes);
    bytes
}

/// Shared behavior between controller and node handshake roles.
#[async_trait]
pub trait HandshakeParticipant {
    async fn run<T: HandshakeTransport + Send>(
        &self,
        transport: &mut T,
    ) -> Result<HandshakeOutcome, HandshakeError>;
}

/// Minimal authenticator stub for challenge validation.
pub trait ChallengeAuthenticator {
    fn sign_challenge(&self, nonce: &[u8]) -> Vec<u8>;
    fn verify_challenge(&self, nonce: &[u8], signature: &[u8]) -> bool;
}

/// Output returned by handshake drivers.
#[derive(Debug, Clone)]
pub struct HandshakeOutcome {
    pub established: SessionEstablished,
    pub keys: SessionKeys,
}