use anyhow::{Context, Result};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::Mutex;
use tracing::{debug, trace};
use aes::Aes128;
use ctr::cipher::{KeyIvInit, StreamCipher};
use ed25519_dalek::SigningKey;
use hkdf::Hkdf;
use hmac::{Hmac, Mac};
use sha2::{Digest, Sha256};
use x25519_dalek::{EphemeralSecret, PublicKey as X25519PublicKey};
type HmacSha256 = Hmac<Sha256>;
type Aes128Ctr = ctr::Ctr128BE<Aes128>;
#[derive(Debug, Clone)]
pub struct CircuitStats {
pub circuit_id: CircuitId,
pub created_at: crate::utils::clock::Instant,
pub last_activity: crate::utils::clock::Instant,
pub bytes_sent: u64,
pub bytes_received: u64,
pub active_streams: usize,
}
#[derive(Debug, Clone)]
pub struct RelayStats {
pub total_circuits: usize,
pub total_streams: usize,
pub total_bytes_sent: u64,
pub total_bytes_received: u64,
pub circuit_stats: Vec<CircuitStats>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct CircuitId(u32);
impl CircuitId {
pub fn new(id: u32) -> Self {
Self(id)
}
pub fn as_u32(&self) -> u32 {
self.0
}
pub fn from_bytes(bytes: &[u8; 4]) -> Self {
Self(u32::from_be_bytes(*bytes))
}
pub fn to_bytes(&self) -> [u8; 4] {
self.0.to_be_bytes()
}
}
impl std::fmt::Display for CircuitId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "0x{:08x}", self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct StreamId(u16);
impl StreamId {
pub fn new(id: u16) -> Self {
Self(id)
}
pub fn as_u16(&self) -> u16 {
self.0
}
}
pub const CIRCUIT_WINDOW_START: u16 = 1000;
pub const CIRCUIT_WINDOW_INCREMENT: u16 = 100;
#[derive(Debug)]
pub struct Circuit {
pub id: CircuitId,
pub crypto: CircuitCrypto,
pub stream_manager: crate::server::tor_relay::stream::StreamManager,
pub next_hop: Option<String>,
pub created_at: crate::utils::clock::Instant,
pub last_activity: crate::utils::clock::Instant,
pub bytes_sent: u64,
pub bytes_received: u64,
pub package_window: u16,
pub deliver_window: u16,
pub relay_cells_received: u16,
}
impl Circuit {
pub fn new(id: CircuitId, key_material: KeyMaterial) -> Self {
let now = crate::utils::clock::Instant::now();
Self {
id,
crypto: CircuitCrypto::new(key_material),
stream_manager: crate::server::tor_relay::stream::StreamManager::new(),
next_hop: None,
created_at: now,
last_activity: now,
bytes_sent: 0,
bytes_received: 0,
package_window: CIRCUIT_WINDOW_START,
deliver_window: CIRCUIT_WINDOW_START,
relay_cells_received: 0,
}
}
pub fn record_sent(&mut self, bytes: u64) {
self.bytes_sent += bytes;
self.last_activity = crate::utils::clock::Instant::now();
}
pub fn record_received(&mut self, bytes: u64) {
self.bytes_received += bytes;
self.last_activity = crate::utils::clock::Instant::now();
}
pub fn decrypt_relay_cell(&mut self, payload: &mut [u8]) -> Result<()> {
self.crypto.decrypt(payload)
}
pub fn encrypt_relay_cell(&mut self, payload: &mut [u8]) -> Result<()> {
self.crypto.encrypt(payload)
}
pub fn record_relay_received(&mut self) -> bool {
self.deliver_window = self.deliver_window.saturating_sub(1);
self.relay_cells_received += 1;
if self.relay_cells_received >= CIRCUIT_WINDOW_INCREMENT {
self.relay_cells_received = 0;
self.deliver_window += CIRCUIT_WINDOW_INCREMENT;
return true;
}
false
}
pub fn process_circuit_sendme(&mut self) {
self.package_window += CIRCUIT_WINDOW_INCREMENT;
trace!(
"Circuit {} package window increased to {}",
self.id.as_u32(),
self.package_window
);
}
pub fn can_send_relay(&self) -> bool {
self.package_window > 0
}
pub fn consume_package_window(&mut self) {
self.package_window = self.package_window.saturating_sub(1);
}
}
pub struct CircuitCrypto {
forward_cipher: Aes128Ctr,
backward_cipher: Aes128Ctr,
forward_digest: Sha256,
backward_digest: Sha256,
}
impl std::fmt::Debug for CircuitCrypto {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CircuitCrypto")
.field("forward_cipher", &"<cipher>")
.field("backward_cipher", &"<cipher>")
.field("forward_digest", &"<digest>")
.field("backward_digest", &"<digest>")
.finish()
}
}
impl CircuitCrypto {
pub fn new(keys: KeyMaterial) -> Self {
let forward_cipher = Aes128Ctr::new(&keys.kf.into(), &[0u8; 16].into());
let backward_cipher = Aes128Ctr::new(&keys.kb.into(), &[0u8; 16].into());
let mut forward_digest = Sha256::new();
forward_digest.update(&keys.df);
let mut backward_digest = Sha256::new();
backward_digest.update(&keys.db);
Self {
forward_cipher,
backward_cipher,
forward_digest,
backward_digest,
}
}
pub fn decrypt(&mut self, payload: &mut [u8]) -> Result<()> {
self.forward_cipher.apply_keystream(payload);
self.forward_digest.update(&*payload);
Ok(())
}
pub fn encrypt(&mut self, payload: &mut [u8]) -> Result<()> {
self.backward_digest.update(&*payload);
self.backward_cipher.apply_keystream(payload);
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct KeyMaterial {
pub kf: [u8; 16],
pub kb: [u8; 16],
pub df: [u8; 20],
pub db: [u8; 20],
}
const PROTOID: &[u8] = b"ntor-curve25519-sha256-1";
const T_MAC: &[u8] = b"ntor-curve25519-sha256-1:mac";
const T_KEY: &[u8] = b"ntor-curve25519-sha256-1:key_extract";
const T_VERIFY: &[u8] = b"ntor-curve25519-sha256-1:verify";
const M_EXPAND: &[u8] = b"ntor-curve25519-sha256-1:key_expand";
pub struct NtorServer {
identity_key: SigningKey,
onion_key: x25519_dalek::StaticSecret,
onion_pubkey: X25519PublicKey,
}
impl NtorServer {
pub fn new() -> Self {
use rand::rngs::OsRng;
let identity_key = SigningKey::generate(&mut OsRng);
let onion_key = x25519_dalek::StaticSecret::random_from_rng(OsRng);
let onion_pubkey = X25519PublicKey::from(&onion_key);
Self {
identity_key,
onion_key,
onion_pubkey,
}
}
pub fn onion_public_key(&self) -> &X25519PublicKey {
&self.onion_pubkey
}
pub fn identity_fingerprint(&self) -> [u8; 20] {
let pubkey = self.identity_key.verifying_key();
let mut hasher = Sha256::new();
hasher.update(pubkey.as_bytes());
let hash = hasher.finalize();
let mut fingerprint = [0u8; 20];
fingerprint.copy_from_slice(&hash[..20]);
fingerprint
}
pub fn server_handshake(
&self,
client_x: &[u8; 32],
) -> Result<([u8; 32], [u8; 32], KeyMaterial)> {
use rand::rngs::OsRng;
let client_pubkey = X25519PublicKey::from(*client_x);
let y = EphemeralSecret::random_from_rng(OsRng);
let big_y = X25519PublicKey::from(&y);
let xy = y.diffie_hellman(&client_pubkey);
let xb = self.onion_key.diffie_hellman(&client_pubkey);
let id = self.identity_fingerprint();
let mut secret_input = Vec::with_capacity(32 + 32 + 20 + 32 + 32 + 32 + PROTOID.len());
secret_input.extend_from_slice(xy.as_bytes());
secret_input.extend_from_slice(xb.as_bytes());
secret_input.extend_from_slice(&id);
secret_input.extend_from_slice(self.onion_pubkey.as_bytes());
secret_input.extend_from_slice(client_x);
secret_input.extend_from_slice(big_y.as_bytes());
secret_input.extend_from_slice(PROTOID);
trace!("ntor secret_input length: {}", secret_input.len());
let mut mac = HmacSha256::new_from_slice(T_KEY).context("Failed to create HMAC")?;
mac.update(&secret_input);
let key_seed = mac.finalize().into_bytes();
let mut mac = HmacSha256::new_from_slice(T_VERIFY).context("Failed to create HMAC")?;
mac.update(&secret_input);
let verify = mac.finalize().into_bytes();
let mut auth_input = Vec::with_capacity(32 + 20 + 32 + 32 + 32 + PROTOID.len() + 6);
auth_input.extend_from_slice(&verify);
auth_input.extend_from_slice(&id);
auth_input.extend_from_slice(self.onion_pubkey.as_bytes());
auth_input.extend_from_slice(big_y.as_bytes());
auth_input.extend_from_slice(client_x);
auth_input.extend_from_slice(PROTOID);
auth_input.extend_from_slice(b"Server");
let mut mac = HmacSha256::new_from_slice(T_MAC).context("Failed to create HMAC")?;
mac.update(&auth_input);
let auth = mac.finalize().into_bytes();
let hkdf = Hkdf::<Sha256>::new(Some(T_KEY), &key_seed);
let mut okm = [0u8; 92];
hkdf.expand(M_EXPAND, &mut okm)
.map_err(|_| anyhow::anyhow!("Failed to expand key material"))?;
let key_material = KeyMaterial {
df: okm[0..20].try_into().unwrap(),
db: okm[20..40].try_into().unwrap(),
kf: okm[40..56].try_into().unwrap(),
kb: okm[56..72].try_into().unwrap(),
};
debug!(
"ntor handshake completed, derived {} bytes of key material",
okm.len()
);
let mut y_bytes = [0u8; 32];
y_bytes.copy_from_slice(big_y.as_bytes());
let mut auth_bytes = [0u8; 32];
auth_bytes.copy_from_slice(&auth);
Ok((y_bytes, auth_bytes, key_material))
}
}
pub struct CircuitManager {
circuits: Arc<Mutex<HashMap<CircuitId, Circuit>>>,
ntor_server: Arc<NtorServer>,
}
impl CircuitManager {
pub fn new() -> Self {
Self {
circuits: Arc::new(Mutex::new(HashMap::new())),
ntor_server: Arc::new(NtorServer::new()),
}
}
pub fn onion_public_key(&self) -> &X25519PublicKey {
self.ntor_server.onion_public_key()
}
pub fn identity_fingerprint(&self) -> [u8; 20] {
self.ntor_server.identity_fingerprint()
}
pub async fn handle_create2(
&self,
circuit_id: CircuitId,
client_x: [u8; 32],
) -> Result<([u8; 32], [u8; 32])> {
debug!("Processing CREATE2 for circuit {}", circuit_id.as_u32());
let (y, auth, key_material) = self.ntor_server.server_handshake(&client_x)?;
let circuit = Circuit::new(circuit_id, key_material);
let mut circuits = self.circuits.lock().await;
circuits.insert(circuit_id, circuit);
debug!("Circuit {} created successfully", circuit_id.as_u32());
Ok((y, auth))
}
pub async fn get_circuit(&self, _id: CircuitId) -> Option<Circuit> {
None
}
pub async fn decrypt_relay_cell(
&self,
circuit_id: CircuitId,
payload: &mut [u8],
) -> Result<()> {
let mut circuits = self.circuits.lock().await;
let circuit = circuits.get_mut(&circuit_id).context("Circuit not found")?;
circuit.decrypt_relay_cell(payload)
}
pub async fn encrypt_relay_cell(
&self,
circuit_id: CircuitId,
payload: &mut [u8],
) -> Result<()> {
let mut circuits = self.circuits.lock().await;
let circuit = circuits.get_mut(&circuit_id).context("Circuit not found")?;
circuit.encrypt_relay_cell(payload)
}
pub async fn destroy_circuit(&self, id: CircuitId) {
let mut circuits = self.circuits.lock().await;
circuits.remove(&id);
debug!("Circuit {} destroyed", id.as_u32());
}
pub async fn circuit_count(&self) -> usize {
let circuits = self.circuits.lock().await;
circuits.len()
}
pub async fn create_stream(
&self,
circuit_id: CircuitId,
stream_id: StreamId,
target: String,
) -> Result<()> {
let mut circuits = self.circuits.lock().await;
let circuit = circuits.get_mut(&circuit_id).context("Circuit not found")?;
circuit.stream_manager.create_stream(stream_id, target)
}
pub async fn create_directory_stream(
&self,
circuit_id: CircuitId,
stream_id: StreamId,
) -> Result<()> {
let mut circuits = self.circuits.lock().await;
let circuit = circuits.get_mut(&circuit_id).context("Circuit not found")?;
circuit
.stream_manager
.create_stream(stream_id, "directory".to_string())?;
let stream = circuit
.stream_manager
.get_mut(stream_id)
.context("Stream not found")?;
stream.set_directory();
Ok(())
}
pub async fn set_stream_active(
&self,
circuit_id: CircuitId,
stream_id: StreamId,
connection: tokio::net::TcpStream,
) -> Result<()> {
let mut circuits = self.circuits.lock().await;
let circuit = circuits.get_mut(&circuit_id).context("Circuit not found")?;
let stream = circuit
.stream_manager
.get_mut(stream_id)
.context("Stream not found")?;
stream.set_active(connection);
Ok(())
}
pub async fn get_stream_writer(
&self,
circuit_id: CircuitId,
stream_id: StreamId,
) -> Result<Option<Arc<tokio::sync::Mutex<tokio::net::tcp::OwnedWriteHalf>>>> {
let circuits = self.circuits.lock().await;
let circuit = circuits.get(&circuit_id).context("Circuit not found")?;
let stream = circuit
.stream_manager
.get(stream_id)
.context("Stream not found")?;
Ok(stream.writer())
}
pub async fn get_stream_reader(
&self,
circuit_id: CircuitId,
stream_id: StreamId,
) -> Result<Option<Arc<tokio::sync::Mutex<tokio::net::tcp::OwnedReadHalf>>>> {
let circuits = self.circuits.lock().await;
let circuit = circuits.get(&circuit_id).context("Circuit not found")?;
let stream = circuit
.stream_manager
.get(stream_id)
.context("Stream not found")?;
Ok(stream.reader())
}
pub async fn is_directory_stream(
&self,
circuit_id: CircuitId,
stream_id: StreamId,
) -> Result<bool> {
let circuits = self.circuits.lock().await;
let circuit = circuits.get(&circuit_id).context("Circuit not found")?;
let stream = circuit
.stream_manager
.get(stream_id)
.context("Stream not found")?;
Ok(stream.is_directory())
}
pub async fn append_directory_data(
&self,
circuit_id: CircuitId,
stream_id: StreamId,
data: &[u8],
) -> Result<()> {
let mut circuits = self.circuits.lock().await;
let circuit = circuits.get_mut(&circuit_id).context("Circuit not found")?;
let stream = circuit
.stream_manager
.get_mut(stream_id)
.context("Stream not found")?;
stream.append_request_data(data);
Ok(())
}
pub async fn get_directory_request(
&self,
circuit_id: CircuitId,
stream_id: StreamId,
) -> Result<Vec<u8>> {
let circuits = self.circuits.lock().await;
let circuit = circuits.get(&circuit_id).context("Circuit not found")?;
let stream = circuit
.stream_manager
.get(stream_id)
.context("Stream not found")?;
Ok(stream.get_request_data().unwrap_or(&[]).to_vec())
}
pub async fn close_stream(&self, circuit_id: CircuitId, stream_id: StreamId) -> Result<()> {
let mut circuits = self.circuits.lock().await;
let circuit = circuits.get_mut(&circuit_id).context("Circuit not found")?;
circuit.stream_manager.remove(stream_id);
Ok(())
}
pub async fn record_sent(&self, circuit_id: CircuitId, bytes: u64) -> Result<()> {
let mut circuits = self.circuits.lock().await;
if let Some(circuit) = circuits.get_mut(&circuit_id) {
circuit.record_sent(bytes);
}
Ok(())
}
pub async fn record_received(&self, circuit_id: CircuitId, bytes: u64) -> Result<()> {
let mut circuits = self.circuits.lock().await;
if let Some(circuit) = circuits.get_mut(&circuit_id) {
circuit.record_received(bytes);
}
Ok(())
}
pub async fn get_circuit_stats(&self, circuit_id: CircuitId) -> Option<CircuitStats> {
let circuits = self.circuits.lock().await;
circuits.get(&circuit_id).map(|circuit| CircuitStats {
circuit_id: circuit.id,
created_at: circuit.created_at,
last_activity: circuit.last_activity,
bytes_sent: circuit.bytes_sent,
bytes_received: circuit.bytes_received,
active_streams: circuit.stream_manager.active_streams().len(),
})
}
pub async fn get_relay_stats(&self) -> RelayStats {
let circuits = self.circuits.lock().await;
let mut total_streams = 0;
let mut total_bytes_sent = 0;
let mut total_bytes_received = 0;
let mut circuit_stats = Vec::new();
for circuit in circuits.values() {
let active_streams = circuit.stream_manager.active_streams().len();
total_streams += active_streams;
total_bytes_sent += circuit.bytes_sent;
total_bytes_received += circuit.bytes_received;
circuit_stats.push(CircuitStats {
circuit_id: circuit.id,
created_at: circuit.created_at,
last_activity: circuit.last_activity,
bytes_sent: circuit.bytes_sent,
bytes_received: circuit.bytes_received,
active_streams,
});
}
RelayStats {
total_circuits: circuits.len(),
total_streams,
total_bytes_sent,
total_bytes_received,
circuit_stats,
}
}
pub async fn list_circuits(&self) -> Vec<CircuitId> {
let circuits = self.circuits.lock().await;
circuits.keys().copied().collect()
}
pub async fn record_relay_received(&self, circuit_id: CircuitId) -> Result<bool> {
let mut circuits = self.circuits.lock().await;
let circuit = circuits.get_mut(&circuit_id).context("Circuit not found")?;
Ok(circuit.record_relay_received())
}
pub async fn process_circuit_sendme(&self, circuit_id: CircuitId) -> Result<()> {
let mut circuits = self.circuits.lock().await;
let circuit = circuits.get_mut(&circuit_id).context("Circuit not found")?;
circuit.process_circuit_sendme();
Ok(())
}
pub async fn record_stream_data_received(
&self,
circuit_id: CircuitId,
stream_id: StreamId,
) -> Result<bool> {
let mut circuits = self.circuits.lock().await;
let circuit = circuits.get_mut(&circuit_id).context("Circuit not found")?;
if let Some(stream) = circuit.stream_manager.get_mut(stream_id) {
Ok(stream.record_data_received())
} else {
Ok(false)
}
}
pub async fn process_stream_sendme(
&self,
circuit_id: CircuitId,
stream_id: StreamId,
) -> Result<()> {
let mut circuits = self.circuits.lock().await;
let circuit = circuits.get_mut(&circuit_id).context("Circuit not found")?;
if let Some(stream) = circuit.stream_manager.get_mut(stream_id) {
stream.process_sendme();
}
Ok(())
}
}