// Copyright 2017 Sopium
// This file is part of WireGuard.rs.
// WireGuard.rs is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
// WireGuard.rs is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with WireGuard.rs. If not, see .
extern crate byteorder;
extern crate noise_protocol;
extern crate noise_sodiumoxide;
extern crate sodiumoxide;
extern crate tai64;
extern crate treebitmap;
use self::byteorder::{ByteOrder, LittleEndian};
use self::noise_protocol::{Cipher, U8Array};
use self::noise_sodiumoxide::ChaCha20Poly1305;
use self::sodiumoxide::randombytes::randombytes_into;
use self::tai64::TAI64N;
use self::treebitmap::{IpLookupTable, IpLookupTableOps};
use protocol::*;
use std::collections::HashMap;
use std::mem::uninitialized;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, UdpSocket};
use std::ops::Deref;
use std::sync::{Arc, Mutex, RwLock};
use std::sync::atomic::{AtomicBool, AtomicU64};
use std::sync::atomic::Ordering::Relaxed;
use std::thread::{Builder, JoinHandle, spawn};
use std::time::{Duration, Instant, SystemTime};
use tun::Tun;
// Some Constants.
// That is, 2 ^ 64 - 2 ^ 16 - 1;
const REKEY_AFTER_MESSAGES: u64 = 0xfffffffffffeffff;
// That is, 2 ^ 64 - 2 ^ 4 - 1;
const REJECT_AFTER_MESSAGES: u64 = 0xffffffffffffffef;
// Timeouts, in seconds.
const REKEY_AFTER_TIME: u64 = 120;
const REJECT_AFTER_TIME: u64 = 180;
const REKEY_TIMEOUT: u64 = 5;
const KEEPALIVE_TIMEOUT: u64 = 10;
// Increase if your MTU is larger...
const BUFSIZE: usize = 1500;
type SharedPeerState = Arc>;
// Locking order:
//
// info > pubkey_map > any peers > id_map > anything else
// any peers > rt4 > rt6
/// State of a WG interface.
pub struct WgState {
info: RwLock,
pubkey_map: RwLock>,
id_map: RwLock>,
// Also should be keep in sync. But these should change less often.
rt4: RwLock>,
rt6: RwLock>,
// The secret used to calc cookie.
cookie_secret: Mutex<([u8; 32], Instant)>,
timer_controller: TimerController,
}
/// Removes `Id` from `id_map` when dropped.
struct IdMapGuard {
wg: Arc,
id: Id,
}
impl Drop for IdMapGuard {
fn drop(&mut self) {
if let Ok(mut id_map) = self.wg.id_map.try_write() {
id_map.remove(&self.id);
} else {
let wg = self.wg.clone();
let id = self.id;
spawn(move || { wg.id_map.write().unwrap().remove(&id); });
}
}
}
impl IdMapGuard {
fn new(wg: Arc, id: Id) -> Self {
Self { wg: wg, id: id }
}
}
/// State of a paticular peer.
struct PeerState {
info: PeerInfo,
last_handshake: Option,
cookie: Option<(Cookie, Instant)>,
last_mac1: Option<[u8; 16]>,
handshake: Option,
// XXX: use a Vec? or ArrayVec?
transport0: Option,
transport1: Option,
transport2: Option,
// Rekey because of send but not recv in...
rekey_no_recv: TimerHandle,
// Keep alive because of recv but not send in...
keep_alive: TimerHandle,
// Persistent keep-alive.
persistent_keep_alive: TimerHandle,
// Clear all sessions if no new handshake in REJECT_AFTER_TIME * 3.
clear: TimerHandle,
}
struct Handshake {
self_id: IdMapGuard,
hs: HS,
// Resend after REKEY_TIMEOUT.
#[allow(dead_code)]
resend: TimerHandle,
}
type SecretKey = ::Key;
/// A WireGuard transport session.
struct Transport {
self_id: IdMapGuard,
peer_id: Id,
is_initiator: bool,
// If we are responder, should not send until received one packet.
is_initiator_or_has_received: AtomicBool,
// Also should not send after REJECT_AFTER_TIME,
// or after REJECT_AFTER_MESSAGES.
not_too_old: AtomicBool,
created: Instant,
send_key: SecretKey,
send_counter: AtomicU64,
recv_key: SecretKey,
recv_ar: Mutex,
}
// TODO determine / detect load.
fn is_under_load() -> bool {
false
}
fn udp_process_handshake_init(wg: Arc, sock: &UdpSocket, p: &[u8], addr: SocketAddr) {
if p.len() != 148 {
return;
}
// Lock info.
let info = wg.info.read().unwrap();
if is_under_load() {
let cookie = calc_cookie(&wg.get_cookie_secret(), addr.to_string().as_bytes());
if !cookie_verify(p, &cookie) {
debug!("Mac2 verify failed, send cookie reply.");
let peer_id = Id::from_slice(&p[4..8]);
let mac1 = &p[116..132];
let reply = cookie_reply(info.psk.as_ref(), &info.pubkey, &cookie, peer_id, mac1);
sock.send_to(&reply, addr).unwrap();
return;
} else {
debug!("Mac2 verify OK.");
}
}
if let Ok(mut r) = process_initiation(info.deref(), p) {
let r_pubkey = r.handshake_state.get_rs().unwrap();
if let Some(peer0) = wg.find_peer_by_pubkey(&r_pubkey) {
// Lock peer.
let mut peer = peer0.write().unwrap();
// Compare timestamp.
if Some(r.timestamp) > peer.last_handshake {
peer.last_handshake = Some(r.timestamp);
} else {
debug!("Handshake timestamp smaller.");
return;
}
let self_id = Id::gen();
let mut response = responde(info.deref(), &mut r, self_id);
// Save mac1.
let mut mac1 = [0u8; 16];
mac1.copy_from_slice(&response[60..76]);
peer.last_mac1 = Some(mac1);
cookie_sign(&mut response, peer.get_cookie());
sock.send_to(&response, addr).unwrap();
let t = Transport::new_from_hs(IdMapGuard::new(wg.clone(), self_id),
r.peer_id,
r.handshake_state);
peer.set_endpoint(addr);
peer.push_transport(t);
// Lock id_map.
wg.id_map.write().unwrap().insert(self_id, peer0.clone());
debug!("Handshake successful as responder.");
} else {
debug!("Get handshake init, but can't find peer by pubkey.");
}
} else {
debug!("Get handshake init, but authentication/decryption failed.");
}
}
fn udp_process_handshake_resp(wg: &WgState, sock: &UdpSocket, p: &[u8], addr: SocketAddr) {
if p.len() != 92 {
return;
}
// Lock info.
let info = wg.info.read().unwrap();
if is_under_load() {
let cookie = calc_cookie(&wg.get_cookie_secret(), addr.to_string().as_bytes());
if !cookie_verify(p, &cookie) {
debug!("Mac2 verify failed, send cookie reply.");
let peer_id = Id::from_slice(&p[4..8]);
let mac1 = &p[60..76];
let reply = cookie_reply(info.psk.as_ref(), &info.pubkey, &cookie, peer_id, mac1);
sock.send_to(&reply, addr).unwrap();
return;
} else {
debug!("Mac2 verify OK.");
}
}
let self_id = Id::from_slice(&p[8..12]);
if let Some(peer0) = wg.find_peer_by_id(self_id) {
let (peer_id, hs) = {
// Lock peer.
let peer = peer0.read().unwrap();
if peer.handshake.is_none() {
debug!("Get handshake response message, but don't know id.");
return;
}
let handshake = peer.handshake.as_ref().unwrap();
if handshake.self_id.id != self_id {
debug!("Get handshake response message, but don't know id.");
return;
}
let mut hs = handshake.hs.clone();
if let Ok(peer_id) = process_response(info.deref(), &mut hs, p) {
(peer_id, hs)
} else {
debug!("Get handshake response message, auth/decryption failed.");
return;
}
// Release peer.
};
debug!("Handshake successful as initiator.");
// Lock peer.
let mut peer = peer0.write().unwrap();
let handle = peer.handshake.take().unwrap().self_id;
let t = Transport::new_from_hs(handle, peer_id, hs);
peer.push_transport(t);
peer.set_endpoint(addr);
// Send an empty packet for key confirmation.
do_keep_alive(&peer, sock);
// Lock id_map.
wg.id_map.write().unwrap().insert(self_id, peer0.clone());
} else {
debug!("Get handshake response message, but don't know id.");
}
}
fn udp_process_cookie_reply(wg: &WgState, p: &[u8]) {
let self_id = Id::from_slice(&p[4..8]);
// Lock info.
let info = wg.info.read().unwrap();
if let Some(peer) = wg.find_peer_by_id(self_id) {
// Lock peer.
let mut peer = peer.write().unwrap();
if let Some(mac1) = peer.last_mac1 {
if let Ok(cookie) = process_cookie_reply(info.psk.as_ref(),
&peer.info.peer_pubkey,
&mac1,
p) {
peer.cookie = Some((cookie, Instant::now()));
} else {
debug!("Process cookie reply: auth/decryption failed.");
}
}
}
}
fn udp_process_transport(wg: &WgState, tun: &Tun, p: &[u8], addr: SocketAddr) {
if p.len() < 32 {
return;
}
let self_id = Id::from_slice(&p[4..8]);
let maybe_peer0 = wg.find_peer_by_id(self_id);
if maybe_peer0.is_none() {
debug!("Get transport message, but don't know id.");
return;
}
let peer0 = maybe_peer0.unwrap();
let should_set_endpoint = {
// Lock peer.
let peer = peer0.read().unwrap();
if let Some(t) = peer.find_transport_by_id(self_id) {
let mut buff: [u8; BUFSIZE] = unsafe { uninitialized() };
let decrypted = &mut buff[..p.len() - 32];
if t.decrypt(p, decrypted).is_ok() {
if let Ok((len, src, _)) = parse_ip_packet(decrypted) {
// Reverse path filtering.
let peer1 = wg.find_peer_by_ip(src);
if peer1.is_none() || !Arc::ptr_eq(&peer0, &peer1.unwrap()) {
debug!("Get transport message: allowed IPs check failed.");
} else {
tun.write(&decrypted[..len as usize]).unwrap();
}
}
peer.on_recv(decrypted.len() == 0);
peer.info.endpoint != Some(addr)
} else {
debug!("Get transport message, decryption failed.");
false
}
} else {
false
}
// Release peer.
};
if should_set_endpoint {
// Lock peer.
peer0.write()
.unwrap()
.set_endpoint(addr);
}
}
/// Start a new thread to recv and process UDP packets.
///
/// This thread runs forever.
pub fn start_udp_processing(wg: Arc, sock: Arc, tun: Arc) -> JoinHandle<()> {
Builder::new().name("UDP".to_string()).spawn(move || {
let mut p = [0u8; BUFSIZE];
loop {
let (len, addr) = sock.recv_from(&mut p).unwrap();
if len < 12 {
continue;
}
let type_ = p[0];
let p = &p[..len];
match type_ {
1 => udp_process_handshake_init(wg.clone(), sock.as_ref(), p, addr),
2 => udp_process_handshake_resp(wg.as_ref(), sock.as_ref(), p, addr),
3 => udp_process_cookie_reply(wg.as_ref(), p),
4 => udp_process_transport(wg.as_ref(), tun.as_ref(), p, addr),
_ => (),
}
}
}).unwrap()
}
// Packets >= MAX_PADDING won't be padded.
// 1280 should be a reasonable conservative choice.
const MAX_PADDING: usize = 1280;
const PADDING_MASK: usize = 0b1111;
fn pad_len(len: usize) -> usize {
if len >= MAX_PADDING {
len
} else {
// Next multiply of 16.
(len & !PADDING_MASK) + if len & PADDING_MASK == 0 {
0
} else {
16
}
}
}
#[cfg(test)]
#[test]
fn padding() {
assert_eq!(pad_len(0), 0);
for i in 1..16 {
assert_eq!(pad_len(i), 16);
}
for i in 17..32 {
assert_eq!(pad_len(i), 32);
}
for i in 1265..1280 {
assert_eq!(pad_len(i), 1280);
}
}
/// Start a new thread to read and process packets from TUN device.
///
/// This thread runs forever.
pub fn start_tun_packet_processing(wg: Arc, sock: Arc, tun: Arc) -> JoinHandle<()> {
Builder::new().name("TUN".to_string()).spawn(move || {
let mut pkt = [0u8; BUFSIZE];
loop {
let len = tun.read(&mut pkt).unwrap();
let padded_len = pad_len(len);
// Do not leak other packets' data!
for b in &mut pkt[len..padded_len] {
*b = 0;
}
let pkt = &pkt[..padded_len];
let parse_result = parse_ip_packet(pkt);
if parse_result.is_err() {
error!("Get packet from TUN device, but failed to parse it!");
continue;
}
let dst = parse_result.unwrap().2;
let peer = wg.find_peer_by_ip(dst);
if peer.is_none() {
// TODO ICMP no route to host.
debug!("No route to host: {}", dst);
continue;
}
let peer0 = peer.unwrap();
let should_handshake = {
// Lock peer.
let peer = peer0.read().unwrap();
if peer.get_endpoint().is_none() {
// TODO ICMP host unreachable?
continue;
}
if let Some(t) = peer.find_transport_to_send() {
let mut encrypted: [u8; BUFSIZE] = unsafe { uninitialized() };
let encrypted = &mut encrypted[..pkt.len() + 32];
let (result, should_handshake) = t.encrypt(pkt, encrypted);
if result.is_ok() {
sock.send_to(encrypted, peer.get_endpoint().unwrap()).unwrap();
peer.on_send(false);
}
// Optimization: don't bother `do_handshake` if there is already
// an ongoing handshake.
should_handshake && peer.handshake.is_none()
} else {
// TODO: queue packets.
// Optimization: don't bother `do_handshake` if there is already
// an ongoing handshake.
peer.handshake.is_none()
}
// Release peer.
};
if should_handshake {
do_handshake(wg.clone(), peer0, sock.clone());
}
}
}).unwrap()
}
/// Start handshake.
///
/// Better not hold any locks when calling this.
//
/// Nothing happens if there is already an ongoing handshake for this peer.
/// Nothing happens if we don't know peer endpoint.
fn do_handshake(wg: Arc, peer0: SharedPeerState, sock: Arc) {
// Lock info.
let info = wg.info.read().unwrap();
// Lock peer.
let mut peer = peer0.write().unwrap();
if peer.handshake.is_some() {
return;
}
let endpoint = if peer.get_endpoint().is_none() {
return;
} else {
peer.get_endpoint().unwrap()
};
debug!("Handshake init.");
let id = Id::gen();
// Lock id_map.
wg.id_map.write().unwrap().insert(id, peer0.clone());
let handle = IdMapGuard::new(wg.clone(), id);
let (mut i, hs) = initiate(info.deref(), &peer.info, id);
cookie_sign(&mut i, peer.get_cookie());
sock.send_to(&i, endpoint).unwrap();
let mut mac1 = [0u8; 16];
mac1.copy_from_slice(&i[116..132]);
peer.last_mac1 = Some(mac1);
let resend = {
let wg = wg.clone();
let sock = sock.clone();
let peer = Arc::downgrade(&peer0);
Box::new(move || {
debug!("Timer: resend.");
peer.upgrade().map(|p| {
p.write().unwrap().handshake = None;
do_handshake(wg.clone(), p, sock.clone());
});
})
};
let resend = wg.timer_controller.register_delay(Duration::from_secs(REKEY_TIMEOUT), resend);
resend.activate();
peer.handshake = Some(Handshake {
self_id: handle,
hs: hs,
resend: resend,
});
peer.clear.adjust_and_activate_if_not_activated(3 * REJECT_AFTER_TIME);
}
fn do_keep_alive(peer: &PeerState, sock: &UdpSocket) {
let e = peer.get_endpoint();
if e.is_none() {
return;
}
let e = e.unwrap();
let t = peer.find_transport_to_send();
if t.is_none() {
return;
}
let t = t.unwrap();
let mut out = [0u8; 32];
if t.encrypt(&[], &mut out).0.is_err() {
return;
}
debug!("Keep alive.");
sock.send_to(&out, e).unwrap();
peer.on_send(true);
}
// Cannot be methods because we need `Arc`.
/// Query state of the WG interface.
pub fn wg_query_state(wg: Arc) -> WgStateOut {
let peers = {
// Lock pubkey map.
let pubkey_map = wg.pubkey_map.read().unwrap();
pubkey_map.values().map(|p| {
// Lock peer.
let peer = p.read().unwrap();
PeerStateOut {
public_key: peer.info.peer_pubkey,
endpoint: peer.info.endpoint,
last_handshake_time: peer.get_last_handshake_time(),
// TODO: Implement tx/rx bytes counting.
rx_bytes: 0,
tx_bytes: 0,
persistent_keepalive_interval: peer.info.keep_alive_interval,
allowed_ips: peer.info.allowed_ips.clone(),
}
// Release peer.
}).collect()
// Release pubkey map.
};
// Lock info.
let info = wg.info.read().unwrap();
WgStateOut {
private_key: info.key.clone(),
public_key: info.pubkey,
preshared_key: info.psk,
peers: peers,
}
// Release info.
}
/// Change WG interface configuration.
///
/// All existing sessions and handshakes will be cleared, on the assumption that
/// our crypto keys has changed!
pub fn wg_change_info(wg: Arc, f: F)
where F: FnOnce(&mut WgInfo),
{
// Lock info.
let mut info = wg.info.write().unwrap();
// Lock pubkey map.
let pubkey_map = wg.pubkey_map.read().unwrap();
for p in pubkey_map.values() {
// Lock peer.
p.write().unwrap().clear();
// Release peer.
}
drop(pubkey_map);
// Release pubkey_map.
f(&mut info);
// Release info.
}
/// Remove a peer.
///
/// Returns: whether there was indeed such a peer, with that public key,
/// that has been removed.
pub fn wg_remove_peer(wg: Arc, peer_pubkey: &X25519Pubkey) -> bool {
// Remove from pubkey_map.
// Lock pubkey_map.
let mut pubkey_map = wg.pubkey_map.write().unwrap();
let p = pubkey_map.remove(peer_pubkey);
if p.is_none() {
// Release pubkey_map.
return false;
}
let p = p.unwrap();
drop(pubkey_map);
// Release pubkey_map.
// Lock peer.
let mut peer = p.write().unwrap();
// This will remove peer from `id_map` through `IdMapGuard`.
peer.clear();
// Remove from rt4 / rt6.
// Lock rt4.
let mut rt4 = wg.rt4.write().unwrap();
// Lock rt6.
let mut rt6 = wg.rt6.write().unwrap();
for &(a, m) in &peer.info.allowed_ips {
match a {
IpAddr::V4(a) => rt4.remove(a, m),
IpAddr::V6(a) => rt6.remove(a, m),
};
}
true
}
/// Change configuration of a peer.
///
/// If (and only if) peer public key is changed, ongoing handshake and all
/// transport sessions will be cleared.
///
/// Returns whether there was indeed such a peer, with that public key,
/// that has been changed.
pub fn wg_change_peer(wg: Arc, peer_pubkey: &X25519Pubkey, f: F) -> bool
where F: FnOnce(&mut PeerInfo),
{
let peer = wg.find_peer_by_pubkey(peer_pubkey);
if peer.is_none() {
return false;
}
let peer0 = peer.unwrap();
// Lock peer.
let mut peer = peer0.write().unwrap();
let old_pubkey = peer.info.peer_pubkey;
let old_allowed_ips = peer.info.allowed_ips.clone();
f(&mut peer.info);
if old_pubkey != peer.info.peer_pubkey {
peer.clear();
}
if old_allowed_ips != peer.info.allowed_ips {
// Lock rt4.
let mut rt4 = wg.rt4.write().unwrap();
// Lock rt6.
let mut rt6 = wg.rt6.write().unwrap();
for (a, m) in old_allowed_ips {
match a {
IpAddr::V4(a) => rt4.remove(a, m),
IpAddr::V6(a) => rt6.remove(a, m),
};
}
for &(a, m) in &peer.info.allowed_ips {
match a {
IpAddr::V4(a4) => rt4.insert(a4, m, peer0.clone()),
IpAddr::V6(a6) => rt6.insert(a6, m, peer0.clone()),
};
}
}
true
}
/// Add a peer to a WG interface.
/// The peer should not already exist.
pub fn wg_add_peer(wg: Arc, peer: &PeerInfo, sock: Arc) {
let register = |a| wg.timer_controller.register_delay(Duration::from_secs(0), a);
let dummy_action = || Box::new(|| {});
// Lock pubkey_map.
let mut pubkey_map = wg.pubkey_map.write().unwrap();
let ps = PeerState {
info: peer.clone(),
last_handshake: None,
last_mac1: None,
cookie: None,
handshake: None,
transport0: None,
transport1: None,
transport2: None,
rekey_no_recv: register(dummy_action()),
keep_alive: register(dummy_action()),
persistent_keep_alive: register(dummy_action()),
clear: register(dummy_action()),
};
let ps = Arc::new(RwLock::new(ps));
// Init timers.
{
let weak_ps = Arc::downgrade(&ps);
// Lock peer.
let mut psw = ps.write().unwrap();
// Same with rekey.
psw.rekey_no_recv = {
let wg = wg.clone();
let weak_ps = weak_ps.clone();
let sock = sock.clone();
register(Box::new(move || {
weak_ps.upgrade().map(|p| {
debug!("Timer: rekey_no_recv.");
do_handshake(wg.clone(), p, sock.clone());
});
}))
};
psw.keep_alive = {
let weak_ps = weak_ps.clone();
let sock = sock.clone();
register(Box::new(move || {
weak_ps.upgrade().map(|p| {
debug!("Timer: keep_alive.");
do_keep_alive(&p.read().unwrap(), &sock);
});
}))
};
psw.persistent_keep_alive = {
let weak_ps = weak_ps.clone();
let sock = sock.clone();
register(Box::new(move || {
weak_ps.upgrade().map(|p| {
debug!("Timer: persistent_keep_alive.");
do_keep_alive(&p.read().unwrap(), &sock);
});
}))
};
psw.clear = {
let weak_ps = weak_ps.clone();
register(Box::new(move || {
weak_ps.upgrade().map(|p| {
debug!("Timer: clear.");
p.write().unwrap().clear();
});
}))
};
}
let mut rt4 = wg.rt4.write().unwrap();
let mut rt6 = wg.rt6.write().unwrap();
for &(a, prefix) in &peer.allowed_ips {
match a {
IpAddr::V4(a4) => rt4.insert(a4, prefix, ps.clone()),
IpAddr::V6(a6) => rt6.insert(a6, prefix, ps.clone()),
};
}
pubkey_map.insert(peer.peer_pubkey, ps);
}
impl WgState {
/// Create a new `WgState` from `WgInfo`.
pub fn new(info: WgInfo) -> WgState {
let mut cookie = [0u8; 32];
randombytes_into(&mut cookie);
WgState {
info: RwLock::new(info),
pubkey_map: RwLock::new(HashMap::with_capacity(1)),
id_map: RwLock::new(HashMap::with_capacity(4)),
rt4: RwLock::new(IpLookupTable::new()),
rt6: RwLock::new(IpLookupTable::new()),
cookie_secret: Mutex::new((cookie, Instant::now())),
timer_controller: TimerController::new(),
}
}
/// Create a new `WgState`, and add some peers.
pub fn new_with_peers(info: WgInfo, peers: &[PeerInfo], sock: Arc) -> Arc {
let wg = Arc::new(WgState::new(info));
for p in peers {
wg_add_peer(wg.clone(), p, sock.clone())
}
wg
}
// These methods helps a lot in avoiding deadlocks.
fn find_peer_by_id(&self, id: Id) -> Option {
self.id_map.read().unwrap().get(&id).cloned()
}
fn find_peer_by_pubkey(&self, pk: &X25519Pubkey) -> Option {
self.pubkey_map.read().unwrap().get(pk).cloned()
}
/// Find peer by ip address, consulting the routing tables.
fn find_peer_by_ip(&self, addr: IpAddr) -> Option {
match addr {
IpAddr::V4(ip4) => {
self.rt4.read().unwrap().longest_match(ip4).map(|x| x.2.clone())
}
IpAddr::V6(ip6) => {
self.rt6.read().unwrap().longest_match(ip6).map(|x| x.2.clone())
}
}
}
fn get_cookie_secret(&self) -> [u8; 32] {
let mut cs = self.cookie_secret.lock().unwrap();
let now = Instant::now();
if now.duration_since(cs.1) <= Duration::from_secs(120) {
cs.0
} else {
randombytes_into(&mut cs.0);
cs.1 = now;
cs.0
}
}
}
impl PeerState {
fn get_endpoint(&self) -> Option {
self.info.endpoint
}
fn set_endpoint(&mut self, a: SocketAddr) {
self.info.endpoint = Some(a)
}
fn get_cookie(&self) -> Option<&Cookie> {
if self.cookie.is_none() {
return None;
}
if self.cookie.as_ref().unwrap().1.elapsed() >= Duration::from_secs(120) {
return None;
}
Some(&self.cookie.as_ref().unwrap().0)
}
fn get_last_handshake_time(&self) -> Option {
self.transport0.as_ref().map(|t| {
let dur = t.created.elapsed();
SystemTime::now() - dur
})
}
fn clear(&mut self) {
self.handshake = None;
self.transport0 = None;
self.transport1 = None;
self.transport2 = None;
self.rekey_no_recv.de_activate();
self.keep_alive.de_activate();
self.persistent_keep_alive.de_activate();
self.clear.de_activate();
}
// rekey = is_initiator.
fn on_new_transport(&self) {
self.clear.adjust_and_activate(3 * REJECT_AFTER_TIME);
self.info.keep_alive_interval.as_ref().map(|i| {
self.persistent_keep_alive.adjust_and_activate(*i as u64);
});
}
fn on_recv(&self, is_keepalive: bool) {
self.rekey_no_recv.de_activate();
if !is_keepalive {
self.keep_alive.adjust_and_activate_if_not_activated(KEEPALIVE_TIMEOUT);
}
}
fn on_send(&self, is_keepalive: bool) {
self.keep_alive.de_activate();
if !is_keepalive {
self.rekey_no_recv.
adjust_and_activate_if_not_activated(KEEPALIVE_TIMEOUT + REKEY_TIMEOUT);
}
self.info.keep_alive_interval.as_ref().map(|i| {
self.persistent_keep_alive.adjust_and_activate(*i as u64);
});
}
fn push_transport(&mut self, t: Transport) {
self.on_new_transport();
self.transport2 = self.transport1.take();
self.transport1 = self.transport0.take();
self.transport0 = Some(t);
}
/// Find a transport to send packet.
fn find_transport_to_send(&self) -> Option<&Transport> {
// If there exists any transport, we rely on timers to init handshake.
if let Some(ref t) = self.transport0 {
if t.get_should_send() {
return Some(t);
}
} else {
return None;
}
if let Some(ref t) = self.transport1 {
if t.get_should_send() {
return Some(t);
}
} else {
return None;
}
if let Some(ref t) = self.transport2 {
if t.get_should_send() {
return Some(t);
}
}
None
}
fn find_transport_by_id(&self, id: Id) -> Option<&Transport> {
if let Some(ref t) = self.transport0 {
if t.get_self_id() == id {
return Some(t);
}
} else {
return None;
}
if let Some(ref t) = self.transport1 {
if t.get_self_id() == id {
return Some(t);
}
} else {
return None;
}
if let Some(ref t) = self.transport2 {
if t.get_self_id() == id {
return Some(t);
}
}
None
}
}
impl Transport {
fn new_from_hs(self_id: IdMapGuard, peer_id: Id, hs: HS) -> Self {
let (x, y) = hs.get_ciphers();
let (s, r) = if hs.get_is_initiator() {
(x, y)
} else {
(y, x)
};
let sk = s.extract().0;
let rk = r.extract().0;
Transport {
self_id: self_id,
peer_id: peer_id,
is_initiator: hs.get_is_initiator(),
is_initiator_or_has_received: AtomicBool::new(hs.get_is_initiator()),
not_too_old: AtomicBool::new(true),
send_key: sk,
recv_key: rk,
created: Instant::now(),
recv_ar: Mutex::new(AntiReplay::new()),
send_counter: AtomicU64::new(0),
}
}
fn get_should_send(&self) -> bool {
self.is_initiator_or_has_received.load(Relaxed) && self.not_too_old.load(Relaxed)
}
fn get_self_id(&self) -> Id {
self.self_id.id
}
/// Expect packet with padding.
///
/// Returns: Whether the operation is successful. Whether we should initiate handshake.
///
/// Length: out.len() = msg.len() + 32.
fn encrypt(&self, msg: &[u8], out: &mut [u8]) -> (Result<(), ()>, bool) {
let c = self.send_counter.fetch_add(1, Relaxed);
let mut should_rekey = false;
if self.is_initiator && c >= REKEY_AFTER_MESSAGES {
should_rekey = true;
}
if c >= REJECT_AFTER_MESSAGES {
self.not_too_old.store(false, Relaxed);
return (Err(()), should_rekey);
}
let age = self.created.elapsed();
if age >= Duration::from_secs(REKEY_AFTER_TIME) {
should_rekey = true;
}
if age >= Duration::from_secs(REJECT_AFTER_TIME) {
self.not_too_old.store(false, Relaxed);
return (Err(()), should_rekey);
}
out[0..4].copy_from_slice(&[4, 0, 0, 0]);
out[4..8].copy_from_slice(self.peer_id.as_slice());
LittleEndian::write_u64(&mut out[8..16], c);
::encrypt(&self.send_key, c, &[], msg, &mut out[16..]);
(Ok(()), should_rekey)
}
/// Returns packet maybe with padding.
///
/// Length: out.len() + 32 = msg.len().
fn decrypt(&self, msg: &[u8], out: &mut [u8]) -> Result<(), ()> {
if self.created.elapsed() >= Duration::from_secs(REJECT_AFTER_TIME) {
return Err(());
}
if msg.len() < 32 {
return Err(());
}
if msg[0..4] != [4, 0, 0, 0] {
return Err(());
}
if self.created.elapsed() >= Duration::from_secs(REJECT_AFTER_TIME) {
return Err(());
}
let counter = LittleEndian::read_u64(&msg[8..16]);
if counter >= REJECT_AFTER_MESSAGES {
return Err(());
}
::decrypt(&self.recv_key, counter, &[], &msg[16..], out)?;
if !self.recv_ar.lock().unwrap().check_and_update(counter) {
return Err(());
}
self.is_initiator_or_has_received.store(true, Relaxed);
Ok(())
}
}