use crate::crypto::{PrivateKey, PublicKey25519, dh_generate};
use crate::initiator::{InitiatorState, SessionKeys};
use crate::messages::{HandshakeInitiation, HandshakeResponse};
use crate::replay::ReplayWindow;
use crate::responder::ResponderState;
use crate::{Result, WireGuardError};
use std::collections::HashMap;
use std::net::SocketAddr;
use std::time::Duration;
pub const REKEY_AFTER_TIME: Duration = Duration::from_secs(120);
pub const REJECT_AFTER_TIME: Duration = Duration::from_secs(180);
pub const KEEPALIVE_TIMEOUT: Duration = Duration::from_secs(10);
#[derive(Debug, Clone)]
pub struct PeerInfo {
pub public_key: PublicKey25519,
pub preshared_key: Option<[u8; 32]>,
pub endpoint: Option<SocketAddr>,
pub allowed_ips: Vec<(std::net::IpAddr, u8)>, pub persistent_keepalive: Option<u16>,
}
#[derive(Debug, Clone)]
pub struct ActiveSession {
pub keys: SessionKeys,
pub peer_public_key: PublicKey25519,
pub created_at: std::time::Instant,
pub last_used: std::time::Instant,
pub last_send: std::time::Instant,
pub endpoint: Option<SocketAddr>,
pub replay_window: ReplayWindow,
}
pub struct WireGuardProtocol {
local_private_key: PrivateKey,
local_public_key: PublicKey25519,
peers: HashMap<PublicKey25519, PeerInfo>,
active_sessions: HashMap<u32, ActiveSession>, pending_initiations: HashMap<u32, InitiatorState>, pending_responses: HashMap<u32, ResponderState>, }
impl WireGuardProtocol {
pub fn new(private_key: Option<PrivateKey>) -> Self {
let (private_key, public_key) = if let Some(key) = private_key {
let public_key = {
use x25519_dalek::{PublicKey, StaticSecret};
let secret = StaticSecret::from(key);
PublicKey::from(&secret).to_bytes()
};
(key, public_key)
} else {
dh_generate()
};
Self {
local_private_key: private_key,
local_public_key: public_key,
peers: HashMap::new(),
active_sessions: HashMap::new(),
pending_initiations: HashMap::new(),
pending_responses: HashMap::new(),
}
}
pub fn public_key(&self) -> PublicKey25519 {
self.local_public_key
}
pub fn add_peer(&mut self, peer_info: PeerInfo) {
self.peers.insert(peer_info.public_key, peer_info);
}
pub fn remove_peer(&mut self, public_key: &PublicKey25519) {
self.peers.remove(public_key);
self.active_sessions
.retain(|_, session| session.peer_public_key != *public_key);
}
pub fn get_peer(&self, public_key: &PublicKey25519) -> Option<&PeerInfo> {
self.peers.get(public_key)
}
pub fn initiate_handshake(
&mut self,
peer_public_key: &PublicKey25519,
) -> Result<HandshakeInitiation> {
let peer_info = self
.peers
.get(peer_public_key)
.ok_or_else(|| WireGuardError::ProtocolError("Unknown peer".to_string()))?;
let mut initiator = InitiatorState::new(
self.local_private_key,
*peer_public_key,
peer_info.preshared_key,
);
let initiation = initiator.create_initiation()?;
let sender_id = initiation.sender;
self.pending_initiations.insert(sender_id, initiator);
Ok(initiation)
}
pub fn process_initiation(&mut self, msg: &HandshakeInitiation) -> Result<HandshakeResponse> {
let mut responder = ResponderState::new(self.local_private_key, None);
let peer_public_key = responder.process_initiation(msg)?;
let peer_info = self
.peers
.get(&peer_public_key)
.ok_or_else(|| WireGuardError::ProtocolError("Unknown peer".to_string()))?;
let mut responder = ResponderState::new(self.local_private_key, peer_info.preshared_key);
let _peer_key = responder.process_initiation(msg)?;
let response = responder.create_response(msg.sender)?;
let sender_id = response.sender;
let keys = responder.derive_keys()?;
let now = std::time::Instant::now();
let session = ActiveSession {
keys,
peer_public_key,
created_at: now,
last_used: now,
last_send: now,
endpoint: None,
replay_window: ReplayWindow::new(),
};
self.active_sessions.insert(sender_id, session);
#[cfg(feature = "transport")]
{
use tracing::info;
let peer_key_hex = hex::encode(peer_public_key);
info!(
session_id = sender_id,
peer_public_key = &peer_key_hex[..8],
"SESSION: New session created (responder)"
);
}
Ok(response)
}
pub fn process_response(&mut self, msg: &HandshakeResponse) -> Result<PublicKey25519> {
let mut initiator = self
.pending_initiations
.remove(&msg.receiver)
.ok_or_else(|| {
WireGuardError::ProtocolError("No pending initiation found".to_string())
})?;
let peer_public_key = initiator.remote_static_public();
let keys = initiator.process_response(msg)?;
let now = std::time::Instant::now();
let session = ActiveSession {
keys,
peer_public_key,
created_at: now,
last_used: now,
last_send: now,
endpoint: None,
replay_window: ReplayWindow::new(),
};
self.active_sessions.insert(msg.sender, session);
#[cfg(feature = "transport")]
{
use tracing::info;
let peer_key_hex = hex::encode(peer_public_key);
info!(
session_id = msg.sender,
peer_public_key = &peer_key_hex[..8],
"SESSION: New session created (initiator)"
);
}
Ok(peer_public_key)
}
pub fn get_session(&self, sender_id: u32) -> Option<&ActiveSession> {
self.active_sessions.get(&sender_id)
}
pub fn get_session_mut(&mut self, sender_id: u32) -> Option<&mut ActiveSession> {
self.active_sessions.get_mut(&sender_id)
}
pub fn check_replay(&mut self, sender_id: u32, counter: u64) -> Result<()> {
let session = self
.get_session_mut(sender_id)
.ok_or_else(|| WireGuardError::ProtocolError("No active session".to_string()))?;
if session.replay_window.check_and_update(counter) {
session.last_used = std::time::Instant::now();
Ok(())
} else {
Err(WireGuardError::ProtocolError("Replay detected".to_string()))
}
}
pub fn active_sessions(&self) -> &HashMap<u32, ActiveSession> {
&self.active_sessions
}
pub fn peers(&self) -> &HashMap<PublicKey25519, PeerInfo> {
&self.peers
}
pub fn cleanup(&mut self) {
let now = std::time::Instant::now();
let session_timeout = std::time::Duration::from_secs(180); let _handshake_timeout = std::time::Duration::from_secs(30);
self.active_sessions
.retain(|_, session| now.duration_since(session.last_used) < session_timeout);
if self.pending_initiations.len() > 100 {
self.pending_initiations.clear();
}
if self.pending_responses.len() > 100 {
self.pending_responses.clear();
}
}
pub fn update_endpoint(&mut self, sender_id: u32, new_endpoint: SocketAddr) {
if let Some(session) = self.active_sessions.get_mut(&sender_id) {
let old_endpoint = session.endpoint;
if old_endpoint != Some(new_endpoint) {
#[cfg(feature = "transport")]
{
use tracing::info;
let peer_key_hex = hex::encode(session.peer_public_key);
info!(
session_id = sender_id,
peer_public_key = &peer_key_hex[..8],
old_endpoint = ?old_endpoint,
new_endpoint = %new_endpoint,
"ROAMING: Endpoint updated for session"
);
}
}
session.endpoint = Some(new_endpoint);
}
}
pub fn update_last_send(&mut self, sender_id: u32) {
if let Some(session) = self.active_sessions.get_mut(&sender_id) {
session.last_send = std::time::Instant::now();
}
}
pub fn needs_rekey(&self, sender_id: u32) -> bool {
if let Some(session) = self.active_sessions.get(&sender_id) {
let age = std::time::Instant::now().duration_since(session.created_at);
age >= REKEY_AFTER_TIME
} else {
false
}
}
pub fn should_reject(&self, sender_id: u32) -> bool {
if let Some(session) = self.active_sessions.get(&sender_id) {
let age = std::time::Instant::now().duration_since(session.created_at);
age >= REJECT_AFTER_TIME
} else {
true
}
}
pub fn find_session_by_peer(&self, peer_public_key: &PublicKey25519) -> Option<(u32, &ActiveSession)> {
self.active_sessions
.iter()
.find(|(_, session)| session.peer_public_key == *peer_public_key)
.map(|(id, session)| (*id, session))
}
pub fn find_session_by_peer_mut(&mut self, peer_public_key: &PublicKey25519) -> Option<(u32, &mut ActiveSession)> {
self.active_sessions
.iter_mut()
.find(|(_, session)| session.peer_public_key == *peer_public_key)
.map(|(id, session)| (*id, session))
}
}