modules encapsulate from each other

This commit is contained in:
2026-03-26 13:19:59 +07:00
parent cb837c08f2
commit c51c086fbc
27 changed files with 194 additions and 175 deletions
+1 -1
View File
@@ -11,7 +11,7 @@ use tokio::{
time::Instant, time::Instant,
}; };
// Добавили trace для частых логов (попакетно) и debug для состояний // Добавили trace для частых логов (попакетно) и debug для состояний
use netrunner_logger::{debug, error, info, trace, warn}; use netrunner_logger::{debug, info, trace, warn};
// ============================================================================ // ============================================================================
// 1. БАЗОВАЯ СТРУКТУРА (ConnectionCore) // 1. БАЗОВАЯ СТРУКТУРА (ConnectionCore)
+2 -2
View File
@@ -1,12 +1,12 @@
use bytes::Bytes; use bytes::Bytes;
use netrunner_core::nrxp::socks::TargetAddress; use netrunner_core::nrxp::TargetAddress;
use netrunner_logger::{debug, error, info, trace, warn}; use netrunner_logger::{debug, error, info, trace, warn};
use smoltcp::{ use smoltcp::{
iface::{SocketHandle, SocketSet}, iface::{SocketHandle, SocketSet},
socket::{AnySocket, icmp, tcp, udp}, socket::{AnySocket, icmp, tcp, udp},
wire::{IpListenEndpoint, IpProtocol, Ipv4Packet, TcpPacket, UdpPacket}, wire::{IpListenEndpoint, IpProtocol, Ipv4Packet, TcpPacket, UdpPacket},
}; };
use std::{collections::HashMap, time::Duration, time::Instant as StdInstant}; use std::{collections::HashMap, time::Instant as StdInstant};
use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{TcpStream, UdpSocket}; use tokio::net::{TcpStream, UdpSocket};
+1 -1
View File
@@ -310,7 +310,7 @@ impl EngineBuilder {
// 4. Подключение к серверу // 4. Подключение к серверу
info!("Establishing secure tunnel to proxy server..."); info!("Establishing secure tunnel to proxy server...");
let muxer = ClientHandler::connect(&self.config.remote_address) ClientHandler::connect(&self.config.remote_address)
.await .await
.map_err(|e| format!("Failed to establish secure tunnel: {}", e))?; .map_err(|e| format!("Failed to establish secure tunnel: {}", e))?;
info!("Secure tunnel established, Muxer is ready."); info!("Secure tunnel established, Muxer is ready.");
+1 -1
View File
@@ -1,6 +1,6 @@
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
pub trait AeadPacker { pub(crate) trait AeadPacker {
fn encrypt(&mut self, data: &mut BytesMut) -> Result<Bytes, chacha20poly1305::aead::Error>; fn encrypt(&mut self, data: &mut BytesMut) -> Result<Bytes, chacha20poly1305::aead::Error>;
fn decrypt(&mut self, data: &mut BytesMut) -> Result<Bytes, chacha20poly1305::aead::Error>; fn decrypt(&mut self, data: &mut BytesMut) -> Result<Bytes, chacha20poly1305::aead::Error>;
} }
+5 -6
View File
@@ -3,8 +3,7 @@ use chacha20poly1305::aead::generic_array::GenericArray;
use chacha20poly1305::{AeadInPlace, ChaCha20Poly1305, Key, KeyInit, Nonce}; use chacha20poly1305::{AeadInPlace, ChaCha20Poly1305, Key, KeyInit, Nonce};
use crate::crypto::aead::AeadPacker; use crate::crypto::aead::AeadPacker;
struct NonceState {
pub struct NonceState {
counter: u64, counter: u64,
base_iv: [u8; 12], base_iv: [u8; 12],
} }
@@ -32,10 +31,10 @@ impl NonceState {
} }
pub struct ChaChaCipher { pub struct ChaChaCipher {
pub encrypt_cipher: ChaCha20Poly1305, encrypt_cipher: ChaCha20Poly1305,
pub decrypt_cipher: ChaCha20Poly1305, decrypt_cipher: ChaCha20Poly1305,
pub encrypt_state: NonceState, encrypt_state: NonceState,
pub decrypt_state: NonceState, decrypt_state: NonceState,
} }
impl ChaChaCipher { impl ChaChaCipher {
+3 -3
View File
@@ -1,12 +1,12 @@
use aead::OsRng; use aead::OsRng;
use x25519_dalek::{EphemeralSecret, PublicKey}; use x25519_dalek::{EphemeralSecret, PublicKey};
pub struct ECDH { pub(crate) struct ECDH {
pub public_key: PublicKey, pub public_key: PublicKey,
pub private_key: Option<EphemeralSecret>, pub private_key: Option<EphemeralSecret>,
} }
impl ECDH { impl ECDH {
pub fn new() -> Self { pub(crate) fn new() -> Self {
let secret = EphemeralSecret::random_from_rng(&mut OsRng); let secret = EphemeralSecret::random_from_rng(&mut OsRng);
let public = PublicKey::from(&secret); let public = PublicKey::from(&secret);
Self { Self {
@@ -15,7 +15,7 @@ impl ECDH {
} }
} }
pub fn get_shared(&mut self, public: &PublicKey) -> Option<[u8; 32]> { pub(crate) fn get_shared(&mut self, public: &PublicKey) -> Option<[u8; 32]> {
let private_key = self.private_key.take()?; let private_key = self.private_key.take()?;
let shared = private_key.diffie_hellman(&public); let shared = private_key.diffie_hellman(&public);
Some(*shared.as_bytes()) Some(*shared.as_bytes())
+3 -3
View File
@@ -1,15 +1,15 @@
use hkdf::Hkdf; use hkdf::Hkdf;
use sha2::Sha256; use sha2::Sha256;
pub struct HKDF; pub(crate) struct HKDF;
impl HKDF { impl HKDF {
pub fn extract_key(salt: &[u8], ikm: &[u8]) -> Hkdf<Sha256> { pub(crate) fn extract_key(salt: &[u8], ikm: &[u8]) -> Hkdf<Sha256> {
let extracted_key = Hkdf::<Sha256>::new(Some(salt), ikm); let extracted_key = Hkdf::<Sha256>::new(Some(salt), ikm);
extracted_key extracted_key
} }
pub fn expand_key<const N: usize>( pub(crate) fn expand_key<const N: usize>(
extracted_key: &Hkdf<Sha256>, extracted_key: &Hkdf<Sha256>,
mark: &[u8], mark: &[u8],
) -> Result<[u8; N], String> { ) -> Result<[u8; N], String> {
+7 -3
View File
@@ -1,5 +1,9 @@
pub mod aead; mod aead;
pub mod chacha; mod chacha;
mod ecdh; mod ecdh;
mod hkdf; mod hkdf;
pub mod session; mod session;
pub(crate) use aead::AeadPacker;
pub(crate) use chacha::ChaChaCipher;
pub(crate) use session::SessionKeys;
+27 -15
View File
@@ -2,7 +2,7 @@ use x25519_dalek::PublicKey;
use crate::{ use crate::{
crypto::{ecdh::ECDH, hkdf::HKDF}, crypto::{ecdh::ECDH, hkdf::HKDF},
tlseng::extension::ExtensionStack, tlseng::ExtensionStack,
}; };
use hmac::{Hmac, Mac}; use hmac::{Hmac, Mac};
@@ -12,14 +12,14 @@ type HmacSha256 = Hmac<Sha256>;
use aead::{rand_core::RngCore, OsRng}; use aead::{rand_core::RngCore, OsRng};
pub struct SaltPair { pub(crate) struct SaltPair {
local_salt: [u8; 32], local_salt: [u8; 32],
remote_salt: [u8; 32], remote_salt: [u8; 32],
is_initiator: bool, is_initiator: bool,
} }
impl SaltPair { impl SaltPair {
pub fn new(is_initiator: bool) -> Self { pub(crate) fn new(is_initiator: bool) -> Self {
let mut local_salt = [0u8; 32]; let mut local_salt = [0u8; 32];
OsRng.fill_bytes(&mut local_salt); OsRng.fill_bytes(&mut local_salt);
Self { Self {
@@ -29,15 +29,15 @@ impl SaltPair {
} }
} }
pub fn get_local(&self) -> [u8; 32] { pub(crate) fn get_local(&self) -> [u8; 32] {
self.local_salt self.local_salt
} }
pub fn set_remote_salt(&mut self, salt: [u8; 32]) { pub(crate) fn set_remote_salt(&mut self, salt: [u8; 32]) {
self.remote_salt = salt self.remote_salt = salt
} }
pub fn get_total(&self) -> [u8; 64] { pub(crate) fn get_total(&self) -> [u8; 64] {
let mut salt = [0u8; 64]; let mut salt = [0u8; 64];
if self.is_initiator { if self.is_initiator {
salt[..32].copy_from_slice(&self.local_salt); salt[..32].copy_from_slice(&self.local_salt);
@@ -52,14 +52,14 @@ impl SaltPair {
} }
pub struct SessionKeys { pub struct SessionKeys {
pub salt: SaltPair, salt: SaltPair,
pub ecdh: ECDH, ecdh: ECDH,
pub auth_key: [u8; 32], auth_key: [u8; 32],
pub current_aead: Option<([u8; 32], [u8; 12], [u8; 32], [u8; 12])>, current_aead: Option<([u8; 32], [u8; 12], [u8; 32], [u8; 12])>,
} }
impl SessionKeys { impl SessionKeys {
pub fn new(is_initiator: bool) -> Self { pub(crate) fn new(is_initiator: bool) -> Self {
Self { Self {
salt: SaltPair::new(is_initiator), salt: SaltPair::new(is_initiator),
ecdh: ECDH::new(), ecdh: ECDH::new(),
@@ -68,12 +68,12 @@ impl SessionKeys {
} }
} }
pub fn get_aead_parameters(&self) -> ([u8; 32], [u8; 12], [u8; 32], [u8; 12]) { pub(crate) fn get_aead_parameters(&self) -> ([u8; 32], [u8; 12], [u8; 32], [u8; 12]) {
self.current_aead self.current_aead
.expect("Keys not generated yet. Call update_keys first.") .expect("Keys not generated yet. Call update_keys first.")
} }
pub fn update_keys( pub(crate) fn update_keys(
&mut self, &mut self,
salt: [u8; 32], salt: [u8; 32],
extensions: &ExtensionStack, extensions: &ExtensionStack,
@@ -176,7 +176,7 @@ impl SessionKeys {
tag tag
} }
pub fn generate_auth_tag(&self) -> [u8; 16] { pub(crate) fn generate_auth_tag(&self) -> [u8; 16] {
let now = std::time::SystemTime::now() let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH) .duration_since(std::time::UNIX_EPOCH)
.unwrap() .unwrap()
@@ -185,7 +185,7 @@ impl SessionKeys {
Self::compute_tag(&self.auth_key, now / 60) Self::compute_tag(&self.auth_key, now / 60)
} }
pub fn verify_auth_tag(&self, received_tag: &[u8; 16]) -> bool { pub(crate) fn verify_auth_tag(&self, received_tag: &[u8; 16]) -> bool {
let now = std::time::SystemTime::now() let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH) .duration_since(std::time::UNIX_EPOCH)
.expect("Time went backwards") .expect("Time went backwards")
@@ -208,4 +208,16 @@ impl SessionKeys {
); );
false false
} }
pub(crate) fn local_salt(&self) -> [u8; 32] {
self.salt.get_local()
}
pub(crate) fn public_key_bytes(&self) -> [u8; 32] {
self.ecdh.public_key.to_bytes()
}
pub(crate) fn auth_key_fingerprint(&self) -> String {
hex::encode(&self.auth_key[..4])
}
} }
+1 -1
View File
@@ -1,6 +1,6 @@
use crate::net::connection::muxer::{MuxMessage, Muxer}; use crate::net::connection::muxer::{MuxMessage, Muxer};
use crate::net::network::NetworkConfig; use crate::net::network::NetworkConfig;
use crate::nrxp::frame::FrameType; use crate::nrxp::FrameType;
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use netrunner_logger::{debug, error}; use netrunner_logger::{debug, error};
use tokio::net::UdpSocket; use tokio::net::UdpSocket;
+5 -9
View File
@@ -6,13 +6,10 @@ use crate::{
network::NetworkConfig, network::NetworkConfig,
}, },
nrxp::{ nrxp::{
codec::Codec, Codec, ErrorAction, FrameType, {SocksReply, SocksRequest},
errors::ErrorAction,
frame::FrameType,
socks::{SocksReply, SocksRequest},
}, },
parser::Parser, parser::Parser,
tlseng::profile::BrowserProfile, tlseng::BrowserProfile,
}; };
use bytes::BytesMut; use bytes::BytesMut;
use netrunner_logger::{info, warn}; use netrunner_logger::{info, warn};
@@ -93,8 +90,8 @@ impl Connection {
} }
pub struct ClientHandler { pub struct ClientHandler {
pub conn: Connection, pub(crate) conn: Connection,
pub muxer: Muxer, pub(crate) muxer: Muxer,
} }
impl ClientHandler { impl ClientHandler {
@@ -249,8 +246,7 @@ impl TunnelHandler for ClientHandler {
} }
pub struct ServerHandler { pub struct ServerHandler {
pub conn: Connection, pub(crate) conn: Connection,
pub token: CancellationToken,
} }
impl ServerHandler { impl ServerHandler {
async fn handle_stealth_fallback( async fn handle_stealth_fallback(
+1 -1
View File
@@ -11,7 +11,7 @@ use tokio_util::sync::CancellationToken;
use crate::{ use crate::{
net::connection::{handler::StreamHandler, muxer::MuxMessage}, net::connection::{handler::StreamHandler, muxer::MuxMessage},
nrxp::{codec::Codec, errors::ErrorAction, frame::FrameType}, nrxp::{Codec, ErrorAction, FrameType},
}; };
pub(crate) struct TunnelEngine { pub(crate) struct TunnelEngine {
+1 -2
View File
@@ -11,8 +11,7 @@ use crate::{
network::NetworkConfig, network::NetworkConfig,
}, },
nrxp::{ nrxp::{
frame::{Frame, FrameType}, SocksReply, {Frame, FrameType},
socks::SocksReply,
}, },
}; };
+1 -1
View File
@@ -4,7 +4,7 @@ use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::mpsc::{error::SendError, Sender}; use tokio::sync::mpsc::{error::SendError, Sender};
use crate::nrxp::frame::FrameType; use crate::nrxp::FrameType;
struct IdGenerator { struct IdGenerator {
counter: AtomicU32, counter: AtomicU32,
+2 -2
View File
@@ -2,7 +2,7 @@ use std::sync::OnceLock;
use crate::{ use crate::{
net::connection::{ClientHandler, Connection, ConnectionRole, ServerHandler, TunnelHandler}, net::connection::{ClientHandler, Connection, ConnectionRole, ServerHandler, TunnelHandler},
nrxp::frame::{FRAME_HEADER_SIZE, MAX_PADDING_SIZE}, nrxp::{FRAME_HEADER_SIZE, MAX_PADDING_SIZE},
}; };
use netrunner_logger::{error, info}; use netrunner_logger::{error, info};
use tokio::net::TcpListener; use tokio::net::TcpListener;
@@ -75,7 +75,7 @@ impl Network {
res = listener.accept() => { res = listener.accept() => {
if let Ok((stream, client_addr)) = res { if let Ok((stream, client_addr)) = res {
let conn = Connection::new(stream, true); let conn = Connection::new(stream, true);
let handler = ServerHandler { conn, token: token.clone() }; let handler = ServerHandler { conn };
tokio::spawn(async move { tokio::spawn(async move {
if let Err(e) = handler.run().await { if let Err(e) = handler.run().await {
error!(client = %client_addr, error = %e, "Server handler error"); error!(client = %client_addr, error = %e, "Server handler error");
+11 -11
View File
@@ -1,14 +1,14 @@
use crate::crypto::session::SessionKeys; use crate::crypto::SessionKeys;
use crate::nrxp::errors::{ErrorAction, ErrorStage, TlsError}; use crate::nrxp::errors::{ErrorAction, ErrorStage, TlsError};
use crate::parser::Parser; use crate::parser::Parser;
use crate::tlseng::extension::ExtensionStack; use crate::tlseng::ExtensionStack;
use crate::tlseng::handshake::{ClientHello, HelloHeader, ServerHello}; use crate::tlseng::{ApplicationData, TlsRecord};
use crate::tlseng::profile::{BrowserProfile, ServerProfile}; use crate::tlseng::{BrowserProfile, ServerProfile};
use crate::tlseng::tls_record::{ApplicationData, TlsRecord}; use crate::tlseng::{ClientHello, HelloHeader, ServerHello};
use crate::tlseng::types::{ContentType, HelloType}; use crate::tlseng::{ContentType, HelloType};
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
pub trait TlsInterceptor { trait TlsInterceptor {
type Output; type Output;
fn start_process(buffer: &mut BytesMut) -> Result<Option<Self::Output>, TlsError> { fn start_process(buffer: &mut BytesMut) -> Result<Option<Self::Output>, TlsError> {
@@ -22,7 +22,7 @@ pub trait TlsInterceptor {
fn handle_record(record: TlsRecord) -> Result<Option<Self::Output>, TlsError>; fn handle_record(record: TlsRecord) -> Result<Option<Self::Output>, TlsError>;
} }
pub enum HandshakeMessage { pub(crate) enum HandshakeMessage {
Client { Client {
base: ClientHello, base: ClientHello,
extensions: ExtensionStack, extensions: ExtensionStack,
@@ -122,7 +122,7 @@ impl TlsInterceptor for ApplicationData {
} }
} }
pub struct TlsBridge; pub(crate) struct TlsBridge;
impl TlsBridge { impl TlsBridge {
pub fn unpack_handshake(buffer: &mut BytesMut) -> Result<Option<HandshakeMessage>, TlsError> { pub fn unpack_handshake(buffer: &mut BytesMut) -> Result<Option<HandshakeMessage>, TlsError> {
@@ -173,12 +173,12 @@ impl TlsBridge {
) )
})?; })?;
let server_pub_key = keys.ecdh.public_key.to_bytes(); let server_pub_key = keys.public_key_bytes();
Ok(ServerHello::make_server_hello( Ok(ServerHello::make_server_hello(
base, base,
&server_pub_key, &server_pub_key,
keys.salt.get_local(), keys.local_salt(),
profile, profile,
)) ))
} else { } else {
+16 -35
View File
@@ -1,22 +1,22 @@
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use crate::crypto::aead::AeadPacker; use crate::crypto::AeadPacker;
use crate::crypto::chacha::ChaChaCipher; use crate::crypto::ChaChaCipher;
use crate::crypto::session::SessionKeys; use crate::crypto::SessionKeys;
use crate::nrxp::bridge::TlsBridge; use crate::nrxp::bridge::TlsBridge;
use crate::nrxp::errors::{ErrorAction, ErrorStage, TlsError}; use crate::nrxp::errors::{ErrorAction, ErrorStage, TlsError};
use crate::nrxp::frame::{Frame, FrameHeader, FrameType, Padding}; use crate::nrxp::frame::{Frame, FrameType};
use crate::parser::Parser; use crate::parser::Parser;
use crate::tlseng::profile::{BrowserProfile, ServerProfile}; use crate::tlseng::{BrowserProfile, ServerProfile};
pub struct Codec { pub struct Codec {
crypto: ChaChaCipher, crypto: ChaChaCipher,
pub session_keys: SessionKeys, session_keys: SessionKeys,
staging: BytesMut, staging: BytesMut,
} }
impl Codec { impl Codec {
pub fn new(is_initiator: bool) -> Self { pub(crate) fn new(is_initiator: bool) -> Self {
Self { Self {
crypto: ChaChaCipher::new(), crypto: ChaChaCipher::new(),
session_keys: SessionKeys::new(is_initiator), session_keys: SessionKeys::new(is_initiator),
@@ -24,7 +24,7 @@ impl Codec {
} }
} }
pub fn make_client_handshake( pub(crate) fn make_client_handshake(
&mut self, &mut self,
profile: &BrowserProfile, profile: &BrowserProfile,
host: &str, host: &str,
@@ -36,7 +36,10 @@ impl Codec {
)) ))
} }
pub fn make_server_handshake(&mut self, buffer: &mut BytesMut) -> Result<Bytes, TlsError> { pub(crate) fn make_server_handshake(
&mut self,
buffer: &mut BytesMut,
) -> Result<Bytes, TlsError> {
let client_msg = TlsBridge::unpack_handshake(buffer)?.ok_or_else(|| { let client_msg = TlsBridge::unpack_handshake(buffer)?.ok_or_else(|| {
TlsError::new( TlsError::new(
ErrorStage::Handshake("No CH"), ErrorStage::Handshake("No CH"),
@@ -57,7 +60,7 @@ impl Codec {
Ok(server_hello_record) Ok(server_hello_record)
} }
pub fn process_handshake(&mut self, buffer: &mut BytesMut) -> Result<(), TlsError> { pub(crate) fn process_handshake(&mut self, buffer: &mut BytesMut) -> Result<(), TlsError> {
let mes_opt = TlsBridge::unpack_handshake(buffer)?; let mes_opt = TlsBridge::unpack_handshake(buffer)?;
let mes = mes_opt.ok_or_else(|| { let mes = mes_opt.ok_or_else(|| {
TlsError::new( TlsError::new(
@@ -83,43 +86,21 @@ impl Codec {
Ok(()) Ok(())
} }
pub async fn try_handshake(&mut self, buffer: &mut BytesMut) -> Result<bool, TlsError> {
match self.process_handshake(buffer) {
Ok(_) => Ok(true),
Err(e) if e.action == ErrorAction::Wait => Ok(false),
Err(e) => Err(e),
}
}
fn outbound( fn outbound(
&mut self, &mut self,
stream_id: u32, stream_id: u32,
frame_type: FrameType, frame_type: FrameType,
payload: Bytes, payload: Bytes,
) -> Result<Bytes, TlsError> { ) -> Result<Bytes, TlsError> {
let padding = Padding::generate_padding();
let tag = self.session_keys.generate_auth_tag(); let tag = self.session_keys.generate_auth_tag();
netrunner_logger::debug!( netrunner_logger::debug!(
step = %(std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs() / 60), step = %(std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs() / 60),
auth_key_hash = %hex::encode(&self.session_keys.auth_key[..4]), auth_key_hash = %hex::encode(&self.session_keys.auth_key_fingerprint()),
generated_tag = %hex::encode(&tag[..4]), generated_tag = %hex::encode(&tag[..4]),
"OUTBOUND: Generated auth tag" "OUTBOUND: Generated auth tag"
); );
let header = FrameHeader { let frame = Frame::new(stream_id, frame_type, payload);
auth_tag: [0u8; 16],
stream_id,
frame_type,
payload_len: payload.len() as u16,
padding_len: padding.len as u16,
};
let frame = Frame {
header,
payload,
padding: padding.data,
};
let mut frame_bytes = frame.into_bytes(&tag); let mut frame_bytes = frame.into_bytes(&tag);
let encrypted_payload = self.crypto.encrypt(&mut frame_bytes).map_err(|e| { let encrypted_payload = self.crypto.encrypt(&mut frame_bytes).map_err(|e| {
@@ -179,7 +160,7 @@ impl Codec {
if !self.session_keys.verify_auth_tag(&received_tag) { if !self.session_keys.verify_auth_tag(&received_tag) {
netrunner_logger::error!( netrunner_logger::error!(
expected_hash = %hex::encode(&self.session_keys.auth_key[..4]), expected_hash = %hex::encode(&self.session_keys.auth_key_fingerprint()),
received = %hex::encode(&received_tag[..4]), received = %hex::encode(&received_tag[..4]),
"AUTH MISMATCH: Potential replay or MITM attack. Dropping connection." "AUTH MISMATCH: Potential replay or MITM attack. Dropping connection."
); );
+43 -24
View File
@@ -4,7 +4,7 @@ use rand::Rng;
use crate::parser::Parser; use crate::parser::Parser;
pub struct Padding { struct Padding {
pub len: u16, pub len: u16,
pub data: Bytes, pub data: Bytes,
} }
@@ -26,7 +26,7 @@ impl Padding {
} }
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub enum FrameType { pub(crate) enum FrameType {
Connect = 0x00, Connect = 0x00,
Data = 0x01, Data = 0x01,
Close = 0x02, Close = 0x02,
@@ -35,18 +35,18 @@ pub enum FrameType {
UdpData = 0x05, UdpData = 0x05,
} }
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct FrameHeader { pub(crate) struct FrameHeader {
pub auth_tag: [u8; 16], pub(crate) _auth_tag: [u8; 16],
pub stream_id: u32, pub(crate) stream_id: u32,
pub frame_type: FrameType, pub(crate) frame_type: FrameType,
pub payload_len: u16, pub(crate) payload_len: u16,
pub padding_len: u16, pub(crate) padding_len: u16,
} }
pub struct Frame { pub(crate) struct Frame {
pub header: FrameHeader, pub(crate) header: FrameHeader,
pub payload: Bytes, pub(crate) payload: Bytes,
pub padding: Bytes, pub(crate) _padding: Bytes,
} }
const AUTH_TAG_SIZE: u16 = 16; const AUTH_TAG_SIZE: u16 = 16;
@@ -59,25 +59,44 @@ pub const FRAME_HEADER_SIZE: u16 =
AUTH_TAG_SIZE + STREAM_ID_SIZE + FRAME_TYPE_SIZE + PAYLOAD_LEN_SIZE + PADDING_LEN_SIZE; AUTH_TAG_SIZE + STREAM_ID_SIZE + FRAME_TYPE_SIZE + PAYLOAD_LEN_SIZE + PADDING_LEN_SIZE;
impl Frame { impl Frame {
pub fn into_bytes(self, auth_key: &[u8; 16]) -> BytesMut { pub(crate) fn new(stream_id: u32, frame_type: FrameType, payload: Bytes) -> Self {
let updated_padding = Padding::generate_padding(); Self {
let total_size = FRAME_HEADER_SIZE as usize + self.payload.len() + self.padding.len(); header: FrameHeader {
_auth_tag: [0u8; 16],
stream_id,
frame_type,
payload_len: payload.len() as u16,
padding_len: 0,
},
payload,
_padding: Bytes::new(),
}
}
// 3. БЕЗОПАСНАЯ СЕРИАЛИЗАЦИЯ С ИСПРАВЛЕННЫМ БАГОМ РАЗМЕРА
pub(crate) fn into_bytes(mut self, auth_key: &[u8; 16]) -> BytesMut {
let generated_padding = Padding::generate_padding();
// Обновляем заголовок реальной длиной сгенерированного паддинга
self.header.padding_len = generated_padding.len;
// Теперь размер считается правильно!
let total_size =
FRAME_HEADER_SIZE as usize + self.payload.len() + generated_padding.len as usize;
let mut buf = BytesMut::with_capacity(total_size); let mut buf = BytesMut::with_capacity(total_size);
buf.put_slice(auth_key); buf.put_slice(auth_key);
buf.put_u32(self.header.stream_id); buf.put_u32(self.header.stream_id);
buf.put_u8(self.header.frame_type as u8); buf.put_u8(self.header.frame_type as u8);
buf.put_u16(self.header.payload_len); buf.put_u16(self.header.payload_len);
buf.put_u16(updated_padding.len); buf.put_u16(self.header.padding_len);
buf.put(self.payload); buf.put(self.payload);
buf.put(generated_padding.data);
buf.put(updated_padding.data);
buf buf
} }
} }
impl Parser for FrameHeader { impl Parser for FrameHeader {
type Error = String; type Error = String;
@@ -92,8 +111,8 @@ impl Parser for FrameHeader {
let mut header_chunk = bytes.split_to(FRAME_HEADER_SIZE as usize); let mut header_chunk = bytes.split_to(FRAME_HEADER_SIZE as usize);
let mut auth_tag = [0u8; 16]; let mut _auth_tag = [0u8; 16];
header_chunk.copy_to_slice(&mut auth_tag); header_chunk.copy_to_slice(&mut _auth_tag);
let stream_id = header_chunk.get_u32(); let stream_id = header_chunk.get_u32();
@@ -110,7 +129,7 @@ impl Parser for FrameHeader {
let padding_len = header_chunk.get_u16(); let padding_len = header_chunk.get_u16();
Ok(Some(Self { Ok(Some(Self {
auth_tag, _auth_tag,
stream_id, stream_id,
frame_type, frame_type,
payload_len, payload_len,
@@ -156,12 +175,12 @@ impl Parser for Frame {
} }
let payload = bytes.split_to(p_len).freeze(); let payload = bytes.split_to(p_len).freeze();
let padding = bytes.split_to(pad_len).freeze(); let _padding = bytes.split_to(pad_len).freeze();
Ok(Some(Self { Ok(Some(Self {
header, header,
payload, payload,
padding, _padding,
})) }))
} }
} }
+12 -4
View File
@@ -1,5 +1,13 @@
// Скрываем модули
mod bridge; mod bridge;
pub mod codec; mod codec;
pub mod errors; mod errors;
pub mod frame; mod frame;
pub mod socks; mod socks;
// Экспортируем для остального ядра только необходимые типы
pub(crate) use codec::Codec;
pub(crate) use errors::{ErrorAction, ErrorStage, TlsError};
pub(crate) use frame::{Frame, FrameType, FRAME_HEADER_SIZE, MAX_PADDING_SIZE};
pub use socks::TargetAddress;
pub(crate) use socks::{SocksReply, SocksRequest};
+13 -13
View File
@@ -4,19 +4,19 @@ use bytes::{Buf, BufMut, BytesMut};
use crate::parser::Parser; use crate::parser::Parser;
pub const SOCKS5_VERSION: u8 = 0x05; const SOCKS5_VERSION: u8 = 0x05;
pub const REPLY_SUCCESS: u8 = 0x00; const REPLY_SUCCESS: u8 = 0x00;
pub const REPLY_AUTH_FAILURE: u8 = 0xFF; const REPLY_AUTH_FAILURE: u8 = 0xFF;
pub const SOCKS5_MIN_HEADER: usize = 4; const SOCKS5_MIN_HEADER: usize = 4;
pub const ATYP_IPV4: u8 = 0x01; const ATYP_IPV4: u8 = 0x01;
pub const ATYP_DOMAIN: u8 = 0x03; const ATYP_DOMAIN: u8 = 0x03;
pub const ATYP_IPV6: u8 = 0x04; const ATYP_IPV6: u8 = 0x04;
pub const IPV4_SIZE: usize = 4; const IPV4_SIZE: usize = 4;
pub const IPV6_SIZE: usize = 16; const IPV6_SIZE: usize = 16;
pub const PORT_SIZE: usize = 2; const PORT_SIZE: usize = 2;
#[derive(Debug)] #[derive(Debug)]
pub enum SocksRequest { pub(crate) enum SocksRequest {
Handshake { methods: Vec<u8> }, Handshake { methods: Vec<u8> },
Connect { command: u8, target: SocksTarget }, Connect { command: u8, target: SocksTarget },
Unknown, Unknown,
@@ -151,7 +151,7 @@ impl SocksRequest {
} }
#[derive(Debug)] #[derive(Debug)]
pub enum SocksReply { pub(crate) enum SocksReply {
HandshakeSelect { HandshakeSelect {
method: u8, method: u8,
}, },
@@ -181,7 +181,7 @@ impl fmt::Display for TargetAddress {
} }
#[derive(Debug)] #[derive(Debug)]
pub struct SocksTarget { pub(crate) struct SocksTarget {
pub addr: TargetAddress, pub addr: TargetAddress,
} }
+6 -6
View File
@@ -1,10 +1,10 @@
pub const HANDSHAKE_TYPE_CLIENT_HELLO: u8 = 0x01; pub(crate) const HANDSHAKE_TYPE_CLIENT_HELLO: u8 = 0x01;
pub const HANDSHAKE_TYPE_SERVER_HELLO: u8 = 0x02; pub(crate) const HANDSHAKE_TYPE_SERVER_HELLO: u8 = 0x02;
pub const TYPE_HOST_NAME: u8 = 0x00; pub(crate) const TYPE_HOST_NAME: u8 = 0x00;
pub const PSK_DHE_KE_MODE: u8 = 0x01; pub(crate) const PSK_DHE_KE_MODE: u8 = 0x01;
pub const CERT_COMPRESSION_BROTLI: u16 = 0x0002; pub(crate) const CERT_COMPRESSION_BROTLI: u16 = 0x0002;
pub const OCSP_STATUS_TYPE: u8 = 0x01; pub(crate) const OCSP_STATUS_TYPE: u8 = 0x01;
+6 -6
View File
@@ -1,7 +1,7 @@
use bytes::{Buf, BufMut, Bytes, BytesMut}; use bytes::{Buf, BufMut, Bytes, BytesMut};
use crate::{ use crate::{
nrxp::errors::{ErrorAction, ErrorStage, TlsError}, nrxp::{ErrorAction, ErrorStage, TlsError},
parser::Parser, parser::Parser,
tlseng::{ tlseng::{
consts::{CERT_COMPRESSION_BROTLI, OCSP_STATUS_TYPE, PSK_DHE_KE_MODE, TYPE_HOST_NAME}, consts::{CERT_COMPRESSION_BROTLI, OCSP_STATUS_TYPE, PSK_DHE_KE_MODE, TYPE_HOST_NAME},
@@ -11,14 +11,14 @@ use crate::{
}; };
#[derive(Debug)] #[derive(Debug)]
pub struct Extension { pub(crate) struct Extension {
pub etype: u16, pub etype: u16,
pub elen: u16, pub _elen: u16,
pub data: Bytes, pub data: Bytes,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct ExtensionStack { pub(crate) struct ExtensionStack {
pub extensions: Vec<Extension>, pub extensions: Vec<Extension>,
} }
@@ -83,13 +83,13 @@ impl Extension {
pub fn new(etype: u16, data: Bytes) -> Self { pub fn new(etype: u16, data: Bytes) -> Self {
Self { Self {
etype, etype,
elen: data.len() as u16, _elen: data.len() as u16,
data, data,
} }
} }
} }
pub struct ExtensionBuilder { pub(crate) struct ExtensionBuilder {
payload: BytesMut, payload: BytesMut,
} }
+7 -12
View File
@@ -2,8 +2,8 @@ use aead::{rand_core::RngCore, OsRng};
use bytes::{Buf, BufMut, Bytes, BytesMut}; use bytes::{Buf, BufMut, Bytes, BytesMut};
use crate::{ use crate::{
crypto::session::SessionKeys, crypto::SessionKeys,
nrxp::errors::{ErrorAction, ErrorStage, TlsError}, nrxp::{ErrorAction, ErrorStage, TlsError},
parser::Parser, parser::Parser,
tlseng::{ tlseng::{
consts::{HANDSHAKE_TYPE_CLIENT_HELLO, HANDSHAKE_TYPE_SERVER_HELLO}, consts::{HANDSHAKE_TYPE_CLIENT_HELLO, HANDSHAKE_TYPE_SERVER_HELLO},
@@ -15,7 +15,7 @@ use crate::{
utils::u24::{BufExt, U24}, utils::u24::{BufExt, U24},
}; };
pub struct HelloHeader { pub(crate) struct HelloHeader {
pub header_type: HelloType, pub header_type: HelloType,
pub _len: U24, pub _len: U24,
} }
@@ -49,7 +49,7 @@ impl Parser for HelloHeader {
} }
} }
pub struct ClientHello { pub(crate) struct ClientHello {
pub _version: ProtocolVersion, pub _version: ProtocolVersion,
pub random: [u8; 32], pub random: [u8; 32],
@@ -96,7 +96,7 @@ impl ClientHello {
} }
pub fn make_client_hello(profile: &BrowserProfile, host: &str, keys: &SessionKeys) -> Bytes { pub fn make_client_hello(profile: &BrowserProfile, host: &str, keys: &SessionKeys) -> Bytes {
let tls_random = keys.salt.get_local(); let tls_random = keys.local_salt();
let mut session_id_bytes = [0u8; 32]; let mut session_id_bytes = [0u8; 32];
OsRng.fill_bytes(&mut session_id_bytes[..16]); OsRng.fill_bytes(&mut session_id_bytes[..16]);
session_id_bytes[16..].copy_from_slice(&keys.generate_auth_tag()); session_id_bytes[16..].copy_from_slice(&keys.generate_auth_tag());
@@ -109,12 +109,7 @@ impl ClientHello {
let mut ext_builder = ExtensionBuilder::new(); let mut ext_builder = ExtensionBuilder::new();
ext_builder.apply_profile( ext_builder.apply_profile(profile, host, &keys.public_key_bytes(), total_overhead);
profile,
host,
&keys.ecdh.public_key.to_bytes(),
total_overhead,
);
let extensions_bytes = ext_builder.build(); let extensions_bytes = ext_builder.build();
@@ -220,7 +215,7 @@ impl Parser for ClientHello {
} }
} }
pub struct ServerHello { pub(crate) struct ServerHello {
pub version: ProtocolVersion, pub version: ProtocolVersion,
pub random: [u8; 32], pub random: [u8; 32],
pub session_id: Bytes, pub session_id: Bytes,
+11 -5
View File
@@ -1,6 +1,12 @@
mod consts; mod consts;
pub mod extension; mod extension;
pub mod handshake; mod handshake;
pub mod profile; mod profile;
pub mod tls_record; mod tls_record;
pub mod types; mod types;
pub(crate) use extension::ExtensionStack;
pub(crate) use handshake::{ClientHello, HelloHeader, ServerHello};
pub(crate) use profile::{BrowserProfile, ServerProfile};
pub(crate) use tls_record::{ApplicationData, TlsRecord};
pub(crate) use types::{ContentType, HelloType};
+2 -2
View File
@@ -1,7 +1,7 @@
use crate::tlseng::types::{ use crate::tlseng::types::{
ExtensionOrder, ProtocolVersion, TlsGroups, TlsSignatures, TlsVersions, ExtensionOrder, ProtocolVersion, TlsGroups, TlsSignatures, TlsVersions,
}; };
pub struct BrowserProfile { pub(crate) struct BrowserProfile {
pub name: &'static str, pub name: &'static str,
pub groups: TlsGroups, pub groups: TlsGroups,
pub signatures: TlsSignatures, pub signatures: TlsSignatures,
@@ -63,7 +63,7 @@ impl BrowserProfile {
}; };
} }
pub struct ServerProfile { pub(crate) struct ServerProfile {
pub versions: TlsVersions, pub versions: TlsVersions,
pub record_layer_version: ProtocolVersion, pub record_layer_version: ProtocolVersion,
pub cipher_suites: &'static [u16], pub cipher_suites: &'static [u16],
+1 -1
View File
@@ -1,7 +1,7 @@
use bytes::{Buf, BufMut, Bytes, BytesMut}; use bytes::{Buf, BufMut, Bytes, BytesMut};
use crate::{ use crate::{
nrxp::errors::{ErrorAction, ErrorStage, TlsError}, nrxp::{ErrorAction, ErrorStage, TlsError},
parser::Parser, parser::Parser,
tlseng::types::{ContentType, ProtocolVersion}, tlseng::types::{ContentType, ProtocolVersion},
}; };
+5 -5
View File
@@ -1,6 +1,6 @@
#[repr(u8)] #[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ContentType { pub(crate) enum ContentType {
Handshake = 0x16, Handshake = 0x16,
ApplicationData = 0x17, ApplicationData = 0x17,
@@ -23,7 +23,7 @@ impl TryFrom<u8> for ContentType {
#[repr(u16)] #[repr(u16)]
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub enum ProtocolVersion { pub(crate) enum ProtocolVersion {
Tls10 = 0x0301, Tls10 = 0x0301,
Tls12 = 0x0303, Tls12 = 0x0303,
Tls13 = 0x0304, Tls13 = 0x0304,
@@ -45,7 +45,7 @@ impl TryFrom<u16> for ProtocolVersion {
} }
#[derive(Copy, Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]
pub enum HelloType { pub(crate) enum HelloType {
Client = 0x01, Client = 0x01,
Server = 0x02, Server = 0x02,
@@ -64,7 +64,7 @@ impl TryFrom<u8> for HelloType {
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct TlsGroups(pub &'static [u16]); pub(crate) struct TlsGroups(pub &'static [u16]);
impl TlsGroups { impl TlsGroups {
pub const X25519: u16 = 0x001d; pub const X25519: u16 = 0x001d;
@@ -77,7 +77,7 @@ impl TlsGroups {
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct TlsSignatures(pub &'static [u16]); pub(crate) struct TlsSignatures(pub &'static [u16]);
impl TlsSignatures { impl TlsSignatures {
pub const ECDSA_SECP256R1_SHA256: u16 = 0x0403; pub const ECDSA_SECP256R1_SHA256: u16 = 0x0403;