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
+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::parser::Parser;
use crate::tlseng::extension::ExtensionStack;
use crate::tlseng::handshake::{ClientHello, HelloHeader, ServerHello};
use crate::tlseng::profile::{BrowserProfile, ServerProfile};
use crate::tlseng::tls_record::{ApplicationData, TlsRecord};
use crate::tlseng::types::{ContentType, HelloType};
use crate::tlseng::ExtensionStack;
use crate::tlseng::{ApplicationData, TlsRecord};
use crate::tlseng::{BrowserProfile, ServerProfile};
use crate::tlseng::{ClientHello, HelloHeader, ServerHello};
use crate::tlseng::{ContentType, HelloType};
use bytes::{Bytes, BytesMut};
pub trait TlsInterceptor {
trait TlsInterceptor {
type Output;
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>;
}
pub enum HandshakeMessage {
pub(crate) enum HandshakeMessage {
Client {
base: ClientHello,
extensions: ExtensionStack,
@@ -122,7 +122,7 @@ impl TlsInterceptor for ApplicationData {
}
}
pub struct TlsBridge;
pub(crate) struct TlsBridge;
impl TlsBridge {
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(
base,
&server_pub_key,
keys.salt.get_local(),
keys.local_salt(),
profile,
))
} else {
+16 -35
View File
@@ -1,22 +1,22 @@
use bytes::{Bytes, BytesMut};
use crate::crypto::aead::AeadPacker;
use crate::crypto::chacha::ChaChaCipher;
use crate::crypto::session::SessionKeys;
use crate::crypto::AeadPacker;
use crate::crypto::ChaChaCipher;
use crate::crypto::SessionKeys;
use crate::nrxp::bridge::TlsBridge;
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::tlseng::profile::{BrowserProfile, ServerProfile};
use crate::tlseng::{BrowserProfile, ServerProfile};
pub struct Codec {
crypto: ChaChaCipher,
pub session_keys: SessionKeys,
session_keys: SessionKeys,
staging: BytesMut,
}
impl Codec {
pub fn new(is_initiator: bool) -> Self {
pub(crate) fn new(is_initiator: bool) -> Self {
Self {
crypto: ChaChaCipher::new(),
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,
profile: &BrowserProfile,
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(|| {
TlsError::new(
ErrorStage::Handshake("No CH"),
@@ -57,7 +60,7 @@ impl Codec {
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 = mes_opt.ok_or_else(|| {
TlsError::new(
@@ -83,43 +86,21 @@ impl Codec {
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(
&mut self,
stream_id: u32,
frame_type: FrameType,
payload: Bytes,
) -> Result<Bytes, TlsError> {
let padding = Padding::generate_padding();
let tag = self.session_keys.generate_auth_tag();
netrunner_logger::debug!(
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]),
"OUTBOUND: Generated auth tag"
);
let header = FrameHeader {
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 frame = Frame::new(stream_id, frame_type, payload);
let mut frame_bytes = frame.into_bytes(&tag);
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) {
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]),
"AUTH MISMATCH: Potential replay or MITM attack. Dropping connection."
);
+43 -24
View File
@@ -4,7 +4,7 @@ use rand::Rng;
use crate::parser::Parser;
pub struct Padding {
struct Padding {
pub len: u16,
pub data: Bytes,
}
@@ -26,7 +26,7 @@ impl Padding {
}
#[derive(Copy, Clone, Debug)]
pub enum FrameType {
pub(crate) enum FrameType {
Connect = 0x00,
Data = 0x01,
Close = 0x02,
@@ -35,18 +35,18 @@ pub enum FrameType {
UdpData = 0x05,
}
#[derive(Copy, Clone)]
pub struct FrameHeader {
pub auth_tag: [u8; 16],
pub stream_id: u32,
pub frame_type: FrameType,
pub payload_len: u16,
pub padding_len: u16,
pub(crate) struct FrameHeader {
pub(crate) _auth_tag: [u8; 16],
pub(crate) stream_id: u32,
pub(crate) frame_type: FrameType,
pub(crate) payload_len: u16,
pub(crate) padding_len: u16,
}
pub struct Frame {
pub header: FrameHeader,
pub payload: Bytes,
pub padding: Bytes,
pub(crate) struct Frame {
pub(crate) header: FrameHeader,
pub(crate) payload: Bytes,
pub(crate) _padding: Bytes,
}
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;
impl Frame {
pub fn into_bytes(self, auth_key: &[u8; 16]) -> BytesMut {
let updated_padding = Padding::generate_padding();
let total_size = FRAME_HEADER_SIZE as usize + self.payload.len() + self.padding.len();
pub(crate) fn new(stream_id: u32, frame_type: FrameType, payload: Bytes) -> Self {
Self {
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);
buf.put_slice(auth_key);
buf.put_u32(self.header.stream_id);
buf.put_u8(self.header.frame_type as u8);
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(updated_padding.data);
buf.put(generated_padding.data);
buf
}
}
impl Parser for FrameHeader {
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 auth_tag = [0u8; 16];
header_chunk.copy_to_slice(&mut auth_tag);
let mut _auth_tag = [0u8; 16];
header_chunk.copy_to_slice(&mut _auth_tag);
let stream_id = header_chunk.get_u32();
@@ -110,7 +129,7 @@ impl Parser for FrameHeader {
let padding_len = header_chunk.get_u16();
Ok(Some(Self {
auth_tag,
_auth_tag,
stream_id,
frame_type,
payload_len,
@@ -156,12 +175,12 @@ impl Parser for Frame {
}
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 {
header,
payload,
padding,
_padding,
}))
}
}
+12 -4
View File
@@ -1,5 +1,13 @@
// Скрываем модули
mod bridge;
pub mod codec;
pub mod errors;
pub mod frame;
pub mod socks;
mod codec;
mod errors;
mod frame;
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;
pub const SOCKS5_VERSION: u8 = 0x05;
pub const REPLY_SUCCESS: u8 = 0x00;
pub const REPLY_AUTH_FAILURE: u8 = 0xFF;
pub const SOCKS5_MIN_HEADER: usize = 4;
pub const ATYP_IPV4: u8 = 0x01;
pub const ATYP_DOMAIN: u8 = 0x03;
pub const ATYP_IPV6: u8 = 0x04;
pub const IPV4_SIZE: usize = 4;
pub const IPV6_SIZE: usize = 16;
pub const PORT_SIZE: usize = 2;
const SOCKS5_VERSION: u8 = 0x05;
const REPLY_SUCCESS: u8 = 0x00;
const REPLY_AUTH_FAILURE: u8 = 0xFF;
const SOCKS5_MIN_HEADER: usize = 4;
const ATYP_IPV4: u8 = 0x01;
const ATYP_DOMAIN: u8 = 0x03;
const ATYP_IPV6: u8 = 0x04;
const IPV4_SIZE: usize = 4;
const IPV6_SIZE: usize = 16;
const PORT_SIZE: usize = 2;
#[derive(Debug)]
pub enum SocksRequest {
pub(crate) enum SocksRequest {
Handshake { methods: Vec<u8> },
Connect { command: u8, target: SocksTarget },
Unknown,
@@ -151,7 +151,7 @@ impl SocksRequest {
}
#[derive(Debug)]
pub enum SocksReply {
pub(crate) enum SocksReply {
HandshakeSelect {
method: u8,
},
@@ -181,7 +181,7 @@ impl fmt::Display for TargetAddress {
}
#[derive(Debug)]
pub struct SocksTarget {
pub(crate) struct SocksTarget {
pub addr: TargetAddress,
}