big changes
This commit is contained in:
@@ -15,7 +15,6 @@ pub struct NonceState {
|
||||
impl NonceState {
|
||||
pub fn new() -> Self {
|
||||
let nonce = ChaCha20Poly1305::generate_nonce(&mut OsRng);
|
||||
println!("Nonce is {:?}", nonce);
|
||||
Self {
|
||||
counter: 0,
|
||||
nonce,
|
||||
@@ -36,7 +35,6 @@ impl NonceState {
|
||||
self.counter += 1;
|
||||
let counter_bytes = self.counter.to_be_bytes();
|
||||
self.nonce[4..12].copy_from_slice(&counter_bytes);
|
||||
println!("Current Nonce is {:?}", self.nonce);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,8 +72,6 @@ impl AeadPacker for ChaChaCipher {
|
||||
}
|
||||
|
||||
fn decrypt<B: Buffer>(&mut self, data: &mut B) -> Result<(), chacha20poly1305::aead::Error> {
|
||||
println!("Buffer: {:?}", data.as_mut());
|
||||
println!("nonce: {:?}", &self.decrypt_state.nonce);
|
||||
self.cipher
|
||||
.decrypt_in_place(&self.decrypt_state.nonce, &[], data)?;
|
||||
self.decrypt_state.increase_counter();
|
||||
|
||||
@@ -2,7 +2,7 @@ use aead::OsRng;
|
||||
use x25519_dalek::{EphemeralSecret, PublicKey};
|
||||
pub struct ECDH {
|
||||
pub public_key: PublicKey,
|
||||
secret_key: EphemeralSecret,
|
||||
pub private_key: Option<EphemeralSecret>,
|
||||
}
|
||||
|
||||
impl ECDH {
|
||||
@@ -10,13 +10,14 @@ impl ECDH {
|
||||
let secret = EphemeralSecret::random_from_rng(&mut OsRng);
|
||||
let public = PublicKey::from(&secret);
|
||||
Self {
|
||||
secret_key: secret,
|
||||
private_key: Some(secret),
|
||||
public_key: public,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_shared(self, public: &PublicKey) -> [u8; 32] {
|
||||
let shared = self.secret_key.diffie_hellman(&public);
|
||||
*shared.as_bytes()
|
||||
pub fn get_shared(&mut self, public: &PublicKey) -> Option<[u8; 32]> {
|
||||
let private_key = self.private_key.take()?;
|
||||
let shared = private_key.diffie_hellman(&public);
|
||||
Some(*shared.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ impl HKDF {
|
||||
extracted_key
|
||||
}
|
||||
|
||||
pub fn expand<const N: usize>(
|
||||
pub fn expand_key<const N: usize>(
|
||||
extracted_key: &Hkdf<Sha256>,
|
||||
mark: &[u8],
|
||||
) -> Result<[u8; N], String> {
|
||||
|
||||
@@ -3,3 +3,4 @@ pub mod chacha;
|
||||
pub mod ecdh;
|
||||
pub mod hkdf;
|
||||
pub mod hmac;
|
||||
pub mod salt_pair;
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
use aead::{rand_core::RngCore, OsRng};
|
||||
|
||||
pub struct SaltPair {
|
||||
local_salt: [u8; 32],
|
||||
remote_salt: [u8; 32],
|
||||
is_initiator: bool,
|
||||
}
|
||||
|
||||
impl SaltPair {
|
||||
pub fn new(is_initiator: bool) -> Self {
|
||||
let mut local_salt = [0u8; 32];
|
||||
OsRng.fill_bytes(&mut local_salt);
|
||||
Self {
|
||||
local_salt,
|
||||
remote_salt: [0; 32],
|
||||
is_initiator,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_local(&self) -> [u8; 32] {
|
||||
self.local_salt
|
||||
}
|
||||
|
||||
pub fn set_remote_salt(&mut self, salt: [u8; 32]) {
|
||||
self.remote_salt = salt
|
||||
}
|
||||
|
||||
pub fn get_total(&self) -> [u8; 64] {
|
||||
let mut salt = [0u8; 64];
|
||||
if self.is_initiator {
|
||||
salt[..32].copy_from_slice(&self.local_salt);
|
||||
salt[32..].copy_from_slice(&self.remote_salt);
|
||||
salt
|
||||
} else {
|
||||
salt[..32].copy_from_slice(&self.remote_salt);
|
||||
salt[32..].copy_from_slice(&self.local_salt);
|
||||
salt
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub mod tls;
|
||||
@@ -0,0 +1,44 @@
|
||||
use bytes::{Bytes, BytesMut};
|
||||
|
||||
use crate::{
|
||||
protocol::{
|
||||
codec::bridges::tls::tls_interceptor::TlsInterceptor,
|
||||
interceptors::error_interceptor::{ErrorAction, ErrorType, InterceptorError},
|
||||
},
|
||||
tlseng::{application_data::ApplicationData, tls_record::TlsRecord, types::ContentType},
|
||||
};
|
||||
|
||||
impl TlsInterceptor for ApplicationData {
|
||||
type Output = ApplicationData;
|
||||
|
||||
fn handle_record(record: TlsRecord) -> Result<Option<Self::Output>, InterceptorError> {
|
||||
let mut payload = BytesMut::from(record.payload.as_ref());
|
||||
match record.content_type {
|
||||
ContentType::ApplicationData => Self::handle_application_data(&mut payload),
|
||||
_ => {
|
||||
println!("content type byte is: {:?}", record.content_type);
|
||||
Err(InterceptorError::new(
|
||||
ErrorType::ApplicationData("Not Application Data"),
|
||||
ErrorAction::Drop,
|
||||
record.serialize(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplicationData {
|
||||
fn handle_application_data(payload: &mut BytesMut) -> Result<Option<Self>, InterceptorError> {
|
||||
println!("Bytes here?: {:?}", &payload);
|
||||
let data_option = ApplicationData::start_process(payload)?;
|
||||
let data = data_option.ok_or_else(|| {
|
||||
InterceptorError::new(
|
||||
ErrorType::ApplicationData("AppData Err TODO"),
|
||||
ErrorAction::Drop,
|
||||
Bytes::copy_from_slice(&[]),
|
||||
)
|
||||
})?;
|
||||
println!("Получены Application Data: {} байт", payload.len());
|
||||
Ok(Some(data))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use x25519_dalek::PublicKey;
|
||||
|
||||
use crate::{
|
||||
protocol::codec::{
|
||||
bridges::tls::{handshake::HandshakeMessage, tls_interceptor::TlsInterceptor},
|
||||
codec::Codec,
|
||||
},
|
||||
tlseng::{application_data::ApplicationData, consts::EXT_KEY_SHARE, extension::ExtensionStack},
|
||||
};
|
||||
|
||||
pub struct TlsBridge;
|
||||
|
||||
impl TlsBridge {
|
||||
fn process_key_share(extensions: &ExtensionStack, codec: &mut Codec) -> Result<(), String> {
|
||||
if let Some(dh_key) = extensions.find_by_type(EXT_KEY_SHARE) {
|
||||
let mut key = [0u8; 32];
|
||||
if dh_key.len() < 32 {
|
||||
return Err("Key too short".into());
|
||||
}
|
||||
key.copy_from_slice(&dh_key[..32]);
|
||||
|
||||
let public_key = PublicKey::from(key);
|
||||
codec
|
||||
.session_keys
|
||||
.generate_keys(&public_key)
|
||||
.map_err(|e| format!("Key gen error: {:?}", e))?; // Обработка Result
|
||||
Ok(())
|
||||
} else {
|
||||
Err("No key share extension found".into())
|
||||
}
|
||||
}
|
||||
//unpack handshake
|
||||
pub fn make_handshake(buffer: &mut BytesMut, codec: &mut Codec) -> Result<(), String> {
|
||||
let incoming_process = HandshakeMessage::start_process(buffer);
|
||||
println!("buffer {:02x?}", &buffer);
|
||||
let message_option = match incoming_process {
|
||||
Ok(mes) => mes,
|
||||
Err(e) => {
|
||||
println!("Error: {:?}", e);
|
||||
None
|
||||
}
|
||||
};
|
||||
if let Some(message) = message_option {
|
||||
match message {
|
||||
HandshakeMessage::Client { base, extensions } => {
|
||||
codec.session_keys.salt.set_remote_salt(base.random);
|
||||
Self::process_key_share(&extensions, codec)?;
|
||||
}
|
||||
HandshakeMessage::Server { base, extensions } => {
|
||||
codec.session_keys.salt.set_remote_salt(base.random);
|
||||
Self::process_key_share(&extensions, codec)?;
|
||||
}
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unpack_app_data(buffer: &mut BytesMut) -> Result<BytesMut, &str> {
|
||||
println!("What is here?");
|
||||
println!("Data {:?}", &buffer);
|
||||
let incoming_process = ApplicationData::start_process(buffer);
|
||||
let option_bytes = match incoming_process {
|
||||
Ok(mes) => mes,
|
||||
Err(e) => {
|
||||
println!("Error: {:?}", e);
|
||||
None
|
||||
}
|
||||
};
|
||||
if let Some(b) = option_bytes {
|
||||
return Ok(BytesMut::from(b.payload.as_ref()));
|
||||
} else {
|
||||
Err("no data")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pack_in_app_data(buffer: &mut BytesMut) -> Bytes {
|
||||
ApplicationData::make_application_data(buffer)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
use bytes::{Bytes, BytesMut};
|
||||
|
||||
use crate::{
|
||||
protocol::{
|
||||
codec::bridges::tls::tls_interceptor::TlsInterceptor,
|
||||
interceptors::error_interceptor::{ErrorAction, ErrorType, InterceptorError},
|
||||
parser::parser::FrameParser,
|
||||
},
|
||||
tlseng::{
|
||||
extension::ExtensionStack,
|
||||
handshake::{
|
||||
client_hello::ClientHello, hello_header::HelloHeader, server_hello::ServerHello,
|
||||
},
|
||||
tls_record::TlsRecord,
|
||||
types::{ContentType, HelloType},
|
||||
},
|
||||
};
|
||||
|
||||
pub enum HandshakeMessage {
|
||||
Client {
|
||||
base: ClientHello,
|
||||
extensions: ExtensionStack,
|
||||
},
|
||||
Server {
|
||||
base: ServerHello,
|
||||
extensions: ExtensionStack,
|
||||
},
|
||||
}
|
||||
|
||||
impl TlsInterceptor for HandshakeMessage {
|
||||
type Output = HandshakeMessage;
|
||||
|
||||
fn handle_record(record: TlsRecord) -> Result<Option<Self::Output>, InterceptorError> {
|
||||
let mut payload = BytesMut::from(record.payload.as_ref());
|
||||
match record.content_type {
|
||||
ContentType::Handshake => Self::handle_handshake(&mut payload),
|
||||
_ => {
|
||||
// It is strange if in this prcoess not a Handshake
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HandshakeMessage {
|
||||
fn handle_handshake(payload: &mut BytesMut) -> Result<Option<Self>, InterceptorError> {
|
||||
if let Some(header) = HelloHeader::parse(payload)? {
|
||||
match header.header_type {
|
||||
HelloType::Server => {
|
||||
let mut server_hello_body = payload;
|
||||
if let Some(server_hello) = ServerHello::parse(&mut server_hello_body)? {
|
||||
return Self::process_server_hello(server_hello);
|
||||
}
|
||||
}
|
||||
HelloType::Client => {
|
||||
let mut client_hello_body = payload;
|
||||
if let Some(client_hello) = ClientHello::parse(&mut client_hello_body)? {
|
||||
return Self::process_client_hello(client_hello);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn process_server_hello(hello: ServerHello) -> Result<Option<Self>, InterceptorError> {
|
||||
println!("Server Hello получен! Random: {:02x?}", hello.random);
|
||||
|
||||
// Парсим расширения сервера, если нужно
|
||||
let mut ext_bytes = BytesMut::from(hello.extensions.as_ref());
|
||||
let ext_stack_option = ExtensionStack::parse(&mut ext_bytes)?;
|
||||
let ext_stack = ext_stack_option.ok_or_else(|| {
|
||||
InterceptorError::new(
|
||||
ErrorType::Handshake("Extension Err TODO"),
|
||||
ErrorAction::Drop,
|
||||
Bytes::copy_from_slice(&[]),
|
||||
)
|
||||
})?;
|
||||
Ok(Some(HandshakeMessage::Server {
|
||||
base: hello,
|
||||
extensions: ext_stack,
|
||||
}))
|
||||
}
|
||||
|
||||
fn process_client_hello(hello: ClientHello) -> Result<Option<Self>, InterceptorError> {
|
||||
println!("Client Hello получен! Random: {:02x?}", hello.random);
|
||||
|
||||
// Парсим расширения клиента, если нужно
|
||||
let mut ext_bytes = BytesMut::from(hello.extensions.as_ref());
|
||||
let ext_stack_option = ExtensionStack::parse(&mut ext_bytes)?;
|
||||
let ext_stack = ext_stack_option.ok_or_else(|| {
|
||||
InterceptorError::new(
|
||||
ErrorType::Handshake("Extension Err TODO"),
|
||||
ErrorAction::Drop,
|
||||
Bytes::copy_from_slice(&[]),
|
||||
)
|
||||
})?;
|
||||
Ok(Some(HandshakeMessage::Client {
|
||||
base: hello,
|
||||
extensions: ext_stack,
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
mod appdata;
|
||||
pub mod bridge;
|
||||
mod handshake;
|
||||
mod tls_interceptor;
|
||||
@@ -0,0 +1,25 @@
|
||||
use bytes::{Bytes, BytesMut};
|
||||
|
||||
use crate::{
|
||||
protocol::{
|
||||
interceptors::error_interceptor::{ErrorAction, ErrorType, InterceptorError},
|
||||
parser::parser::FrameParser,
|
||||
},
|
||||
tlseng::tls_record::TlsRecord,
|
||||
};
|
||||
|
||||
pub trait TlsInterceptor {
|
||||
type Output;
|
||||
fn start_process(buffer: &mut BytesMut) -> Result<Option<Self::Output>, InterceptorError> {
|
||||
match TlsRecord::parse(buffer) {
|
||||
Ok(Some(record)) => Self::handle_record(record),
|
||||
Ok(None) => Err(InterceptorError::new(
|
||||
ErrorType::Tls("Not full Data"),
|
||||
ErrorAction::Wait,
|
||||
Bytes::new(),
|
||||
)),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
fn handle_record(record: TlsRecord) -> Result<Option<Self::Output>, InterceptorError>;
|
||||
}
|
||||
@@ -1,172 +1,49 @@
|
||||
use aead::rand_core::RngCore;
|
||||
use aead::OsRng;
|
||||
use bytes::{BufMut, Bytes, BytesMut};
|
||||
use bytes::{Bytes, BytesMut};
|
||||
|
||||
use crate::crypto::chacha::ChaChaCipher;
|
||||
use crate::crypto::ecdh::ECDH;
|
||||
use crate::crypto::hmac::generate_auth_tag;
|
||||
use crate::protocol::interceptors::hello_interceptor::extension_interceptor::ExtensionStack;
|
||||
use crate::protocol::interceptors::interceptor::Interceptor;
|
||||
use crate::tlseng::etype::EXT_KEY_SHARE;
|
||||
use crate::tlseng::extension::ExtensionBuilder;
|
||||
use crate::tlseng::profile::profile::BrowserProfile;
|
||||
use crate::tlseng::tls::{
|
||||
ClientHello, ContentType, HelloHeader, HelloType, ProtocolVersion, ServerHello, TlsRecord,
|
||||
};
|
||||
use crate::protocol::codec::bridges::tls::bridge::TlsBridge;
|
||||
use crate::protocol::codec::session_keys::SessionKeys;
|
||||
|
||||
use crate::crypto::aead::AeadPacker;
|
||||
|
||||
pub struct Codec {
|
||||
crypto: ChaChaCipher,
|
||||
crypto: ChaChaCipher, //rename chacha
|
||||
pub session_keys: SessionKeys,
|
||||
}
|
||||
|
||||
impl Codec {
|
||||
pub fn new() -> Self {
|
||||
pub fn new(is_initiator: bool) -> Self {
|
||||
Self {
|
||||
crypto: ChaChaCipher::new(),
|
||||
session_keys: SessionKeys::new(is_initiator),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_handshake(&mut self, profile: &BrowserProfile, host: &str) -> Bytes {
|
||||
// 1. Key Exchange: Generate ECDH pair and get public key
|
||||
let ecdh = ECDH::new();
|
||||
let pub_key = &ecdh.public_key;
|
||||
|
||||
// 2. Authentication: Generate 32 bytes for TLS Random
|
||||
// [16 bytes entropy] + [16 bytes HMAC(timestamp)]
|
||||
let mut tls_random = [0u8; 32];
|
||||
OsRng.fill_bytes(&mut tls_random[..16]);
|
||||
|
||||
let auth_token = generate_auth_tag(&[]);
|
||||
tls_random[16..32].copy_from_slice(&auth_token);
|
||||
|
||||
// 3. Extensions: Build the extensions block using the profile
|
||||
let mut ext_builder = ExtensionBuilder::new();
|
||||
// Pass the public key into the KeyShare extension via apply_profile
|
||||
ext_builder.apply_profile(profile, host, pub_key.as_bytes());
|
||||
let extensions_bytes = ext_builder.build();
|
||||
|
||||
let mut session_id = BytesMut::with_capacity(32);
|
||||
session_id.put_slice(&[0u8; 32]);
|
||||
|
||||
// 4. Assemble ClientHello Handshake message
|
||||
let client_hello = ClientHello {
|
||||
version: ProtocolVersion::Tls12, // Legacy version for compatibility
|
||||
random: tls_random,
|
||||
session_id: session_id.freeze(), // Standard 32-byte session ID
|
||||
cipher_suites: vec![0x1301, 0x1302, 0x1303], // TLS 1.3 suites
|
||||
extensions: extensions_bytes,
|
||||
};
|
||||
|
||||
// 5. Wrap ClientHello into a TLS Record
|
||||
let record = TlsRecord {
|
||||
content_type: ContentType::Handshake,
|
||||
version: ProtocolVersion::Tls10, // Standard for ClientHello records
|
||||
len: client_hello.serialize().len() as u16,
|
||||
payload: client_hello.serialize(),
|
||||
};
|
||||
|
||||
println!("Client Hello: {:02x?}", record.payload.to_vec());
|
||||
|
||||
// Final result: Byte buffer ready for the wire
|
||||
record.serialize()
|
||||
pub fn make_handshake(&mut self, buffer: &mut BytesMut) {
|
||||
println!("Handshake len in codec: {:?}", &buffer.len());
|
||||
TlsBridge::make_handshake(buffer, self);
|
||||
}
|
||||
|
||||
/// Основная точка входа для входящих данных из сокета
|
||||
pub fn process_incoming(&mut self, mut buffer: BytesMut) {
|
||||
// 1. Пытаемся достать TLS Record (самый верхний слой)
|
||||
match TlsRecord::intercept(&mut buffer) {
|
||||
Ok(Some(record)) => {
|
||||
println!(
|
||||
"Получен Record: type={:?}, len={}",
|
||||
record.content_type, record.len
|
||||
);
|
||||
|
||||
// 2. Диспетчеризация по типу контента
|
||||
self.handle_record(record);
|
||||
}
|
||||
Ok(None) => {
|
||||
// Пакет неполный, ждем догрузки в буфер
|
||||
}
|
||||
Err(_e) => {
|
||||
// Заглушка: ошибка парсинга рекорда
|
||||
}
|
||||
pub fn unpack(&mut self, buffer: &mut BytesMut) -> Result<Bytes, String> {
|
||||
println!("App data unpack len in codec?: {:?}", &buffer.len());
|
||||
let mut data = TlsBridge::unpack_app_data(buffer);
|
||||
//self.decrypt(&mut data);
|
||||
match data {
|
||||
Ok(bytes) => Ok(bytes.freeze()),
|
||||
Err(e) => Err(e.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_record(&mut self, record: TlsRecord) {
|
||||
match record.content_type {
|
||||
ContentType::Handshake => {
|
||||
self.handle_handshake(record.payload);
|
||||
}
|
||||
ContentType::ApplicationData => {
|
||||
self.handle_application_data(record.payload);
|
||||
}
|
||||
_ => {
|
||||
// Остальные типы (Alert, ChangeCipherSpec) пока игнорируем
|
||||
}
|
||||
}
|
||||
pub fn pack(&mut self, buffer: &mut BytesMut) -> Bytes {
|
||||
println!("App data len in codec?: {:?}", &buffer.len());
|
||||
TlsBridge::pack_in_app_data(buffer)
|
||||
}
|
||||
|
||||
fn handle_handshake(&mut self, mut payload: Bytes) {
|
||||
// Превращаем Bytes в BytesMut для интерцепторов (без копирования данных)
|
||||
let mut body = BytesMut::from(payload.as_ref());
|
||||
|
||||
// 1. Парсим заголовок Handshake (Type + Length)
|
||||
match HelloHeader::intercept(&mut body) {
|
||||
Ok(Some(header)) => {
|
||||
match header.header_type {
|
||||
HelloType::Server => {
|
||||
let mut server_hello_body = body; // Тут уже откушенное тело
|
||||
if let Ok(Some(server_hello)) =
|
||||
ServerHello::intercept(&mut server_hello_body)
|
||||
{
|
||||
self.process_server_hello(server_hello);
|
||||
}
|
||||
}
|
||||
HelloType::Client => {
|
||||
let mut client_hello_body = body;
|
||||
if let Ok(Some(client_hello)) =
|
||||
ClientHello::intercept(&mut client_hello_body)
|
||||
{
|
||||
self.process_client_hello(client_hello);
|
||||
}
|
||||
}
|
||||
_ => { /* Другие типы Handshake (EncryptedExtensions, Certificate и т.д.) */
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => { /* Заглушка на ошибку */ }
|
||||
}
|
||||
pub fn encrypt(&mut self, data: &mut BytesMut) {
|
||||
self.crypto.encrypt(data);
|
||||
}
|
||||
|
||||
fn process_server_hello(&mut self, hello: ServerHello) {
|
||||
println!("Server Hello получен! Random: {:02x?}", hello.random);
|
||||
|
||||
// Парсим расширения сервера, если нужно
|
||||
let mut ext_bytes = BytesMut::from(hello.extensions.as_ref());
|
||||
if let Ok(Some(stack)) = ExtensionStack::intercept(&mut ext_bytes) {
|
||||
// Например, ищем KeyShare сервера для завершения ECDH
|
||||
if let Some(key_share) = stack.find_by_type(EXT_KEY_SHARE) {
|
||||
println!("Найден KeyShare сервера, длина: {}", key_share.len());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn process_client_hello(&mut self, hello: ClientHello) {
|
||||
println!("Client Hello получен! Random: {:02x?}", hello.random);
|
||||
|
||||
// Парсим расширения сервера, если нужно
|
||||
let mut ext_bytes = BytesMut::from(hello.extensions.as_ref());
|
||||
if let Ok(Some(stack)) = ExtensionStack::intercept(&mut ext_bytes) {
|
||||
if let Some(key_share) = stack.find_by_type(EXT_KEY_SHARE) {
|
||||
println!("Найден KeyShare сервера, длина: {}", key_share.len());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_application_data(&mut self, payload: Bytes) {
|
||||
// Здесь будет логика расшифровки через твой ChaChaCipher
|
||||
// Пока просто выводим длину зашифрованных данных
|
||||
println!("Получены Application Data: {} байт", payload.len());
|
||||
pub fn decrypt(&mut self, data: &mut BytesMut) {
|
||||
self.crypto.decrypt(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use aead::OsRng;
|
||||
use bytes::{Buf, BufMut, Bytes, BytesMut};
|
||||
|
||||
use crate::{crypto::hmac::generate_auth_tag, protocol::codec::padding::Padding};
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
mod bridges;
|
||||
pub mod codec;
|
||||
pub mod frame;
|
||||
mod padding;
|
||||
mod session_keys;
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
use x25519_dalek::PublicKey;
|
||||
|
||||
use crate::crypto::{ecdh::ECDH, hkdf::HKDF, salt_pair::SaltPair};
|
||||
|
||||
pub struct KeyPair {
|
||||
pub aead_key: [u8; 32],
|
||||
pub hmac_key: [u8; 32],
|
||||
}
|
||||
|
||||
pub struct SessionKeys {
|
||||
pub salt: SaltPair,
|
||||
pub ecdh: ECDH,
|
||||
key_pair: KeyPair,
|
||||
}
|
||||
|
||||
impl SessionKeys {
|
||||
pub fn new(is_initiator: bool) -> Self {
|
||||
Self {
|
||||
salt: SaltPair::new(is_initiator),
|
||||
ecdh: ECDH::new(),
|
||||
key_pair: KeyPair {
|
||||
aead_key: [0; 32],
|
||||
hmac_key: [0; 32],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_keys(&mut self, public_key: &PublicKey) -> Result<(), ()> {
|
||||
let shared_key = self.ecdh.get_shared(public_key);
|
||||
match shared_key {
|
||||
Some(key) => {
|
||||
let hkdf = HKDF::extract_key(&self.salt.get_total(), &key);
|
||||
let aead = HKDF::expand_key::<32>(&hkdf, b"aead").map_err(|e| {
|
||||
println!("Aead Key expand Error: {}", e);
|
||||
})?;
|
||||
self.key_pair.aead_key = aead;
|
||||
let hmac: [u8; 32] = HKDF::expand_key::<32>(&hkdf, b"hmac").map_err(|e| {
|
||||
println!("HMAC Key expand Error: {}", e);
|
||||
})?;
|
||||
self.key_pair.hmac_key = hmac;
|
||||
Ok(())
|
||||
}
|
||||
None => {
|
||||
println!("Error while generate keys. No Shared key");
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
use crate::{
|
||||
protocol::interceptors::{
|
||||
error_interceptor::interceptor_error::InterceptorError, interceptor::Interceptor,
|
||||
},
|
||||
tlseng::tls::ApplicationData,
|
||||
};
|
||||
|
||||
impl Interceptor for ApplicationData {
|
||||
type Error = InterceptorError;
|
||||
|
||||
fn can_handle(bytes: &bytes::BytesMut) -> bool {
|
||||
// Application Data не может быть пустым по спецификации (хотя бы 1 байт)
|
||||
!bytes.is_empty()
|
||||
}
|
||||
|
||||
fn intercept(bytes: &mut bytes::BytesMut) -> Result<Option<Self>, Self::Error>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let len = bytes.len();
|
||||
|
||||
if len == 0 {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let payload = bytes.split_to(len).freeze();
|
||||
|
||||
Ok(Some(Self {
|
||||
length: len,
|
||||
payload,
|
||||
}))
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -6,13 +6,14 @@ pub enum ErrorAction {
|
||||
Redirect,
|
||||
Drop,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ErrorType {
|
||||
Tls(&'static str),
|
||||
Handshake(&'static str),
|
||||
ApplicationData(&'static str),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InterceptorError {
|
||||
pub error_type: ErrorType,
|
||||
pub action: ErrorAction,
|
||||
@@ -1 +0,0 @@
|
||||
pub mod interceptor_error;
|
||||
@@ -1,4 +0,0 @@
|
||||
pub mod client_hello_interceptor;
|
||||
pub mod extension_interceptor;
|
||||
pub mod hello_interceptor;
|
||||
pub mod server_hello_interceptor;
|
||||
@@ -1,9 +0,0 @@
|
||||
use bytes::BytesMut;
|
||||
|
||||
pub trait Interceptor {
|
||||
type Error;
|
||||
fn can_handle(bytes: &BytesMut) -> bool;
|
||||
fn intercept(bytes: &mut BytesMut) -> Result<Option<Self>, Self::Error>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
@@ -1,5 +1 @@
|
||||
pub mod application_data_interceptor;
|
||||
mod error_interceptor;
|
||||
pub mod hello_interceptor;
|
||||
pub mod interceptor;
|
||||
pub mod tls_record_interceptor;
|
||||
pub mod error_interceptor;
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
pub mod codec;
|
||||
pub mod interceptors;
|
||||
mod parser;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod parser;
|
||||
mod tls;
|
||||
@@ -0,0 +1,9 @@
|
||||
use bytes::BytesMut;
|
||||
|
||||
pub trait FrameParser {
|
||||
type Error;
|
||||
fn can_parse(bytes: &BytesMut) -> bool;
|
||||
fn parse(bytes: &mut BytesMut) -> Result<Option<Self>, Self::Error>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
use crate::{
|
||||
protocol::{interceptors::error_interceptor::InterceptorError, parser::parser::FrameParser},
|
||||
tlseng::application_data::ApplicationData,
|
||||
};
|
||||
|
||||
impl FrameParser for ApplicationData {
|
||||
type Error = InterceptorError;
|
||||
|
||||
fn can_parse(bytes: &bytes::BytesMut) -> bool {
|
||||
// Application Data не может быть пустым по спецификации (хотя бы 1 байт)
|
||||
!bytes.is_empty()
|
||||
}
|
||||
|
||||
fn parse(bytes: &mut bytes::BytesMut) -> Result<Option<Self>, Self::Error>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let len = bytes.len();
|
||||
|
||||
if len == 0 {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let payload = bytes.split_to(len).freeze();
|
||||
|
||||
Ok(Some(Self { len, payload }))
|
||||
}
|
||||
}
|
||||
+7
-9
@@ -1,32 +1,30 @@
|
||||
use bytes::{Buf, Bytes};
|
||||
|
||||
use crate::{
|
||||
protocol::interceptors::{
|
||||
error_interceptor::interceptor_error::{ErrorAction, ErrorType, InterceptorError},
|
||||
interceptor::Interceptor,
|
||||
protocol::{
|
||||
interceptors::error_interceptor::{ErrorAction, ErrorType, InterceptorError},
|
||||
parser::parser::FrameParser,
|
||||
},
|
||||
tlseng::tls::{ClientHello, ProtocolVersion},
|
||||
tlseng::{handshake::client_hello::ClientHello, types::ProtocolVersion},
|
||||
};
|
||||
|
||||
impl Interceptor for ClientHello {
|
||||
impl FrameParser for ClientHello {
|
||||
type Error = InterceptorError;
|
||||
|
||||
fn can_handle(bytes: &bytes::BytesMut) -> bool {
|
||||
fn can_parse(bytes: &bytes::BytesMut) -> bool {
|
||||
// Мы предполагаем, что HelloHeader уже проверил тип.
|
||||
// Здесь можно проверить минимально допустимый размер ClientHello
|
||||
// (Version 2 + Random 32 + SessionID_len 1 = 35 байт)
|
||||
bytes.len() >= 35
|
||||
}
|
||||
|
||||
fn intercept(bytes: &mut bytes::BytesMut) -> Result<Option<Self>, Self::Error>
|
||||
fn parse(bytes: &mut bytes::BytesMut) -> Result<Option<Self>, Self::Error>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
// 1. Проверяем минимальную длину для базовых полей (до Session ID включительно)
|
||||
if bytes.len() < 35 {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
// 2. Version (2 bytes)
|
||||
let raw_version = bytes.get_u16();
|
||||
let version = ProtocolVersion::try_from(raw_version).map_err(|e| {
|
||||
+7
-12
@@ -1,26 +1,22 @@
|
||||
use bytes::{Buf, Bytes, BytesMut};
|
||||
|
||||
use crate::{
|
||||
protocol::interceptors::{
|
||||
error_interceptor::interceptor_error::{ErrorAction, ErrorType, InterceptorError},
|
||||
interceptor::Interceptor,
|
||||
protocol::{
|
||||
interceptors::error_interceptor::{ErrorAction, ErrorType, InterceptorError},
|
||||
parser::parser::FrameParser,
|
||||
},
|
||||
tlseng::extension::Extension,
|
||||
tlseng::extension::{Extension, ExtensionStack},
|
||||
};
|
||||
|
||||
pub struct ExtensionStack {
|
||||
pub extensions: Vec<Extension>,
|
||||
}
|
||||
|
||||
impl Interceptor for ExtensionStack {
|
||||
impl FrameParser for ExtensionStack {
|
||||
type Error = InterceptorError;
|
||||
|
||||
fn can_handle(bytes: &BytesMut) -> bool {
|
||||
fn can_parse(bytes: &BytesMut) -> bool {
|
||||
// Минимальное расширение: тип(2) + длина(2) = 4 байта
|
||||
bytes.len() >= 4
|
||||
}
|
||||
|
||||
fn intercept(bytes: &mut BytesMut) -> Result<Option<Self>, Self::Error> {
|
||||
fn parse(bytes: &mut BytesMut) -> Result<Option<Self>, Self::Error> {
|
||||
let mut extensions = Vec::new();
|
||||
|
||||
while bytes.remaining() >= 4 {
|
||||
@@ -39,7 +35,6 @@ impl Interceptor for ExtensionStack {
|
||||
let data = bytes.split_to(elen).freeze();
|
||||
extensions.push(Extension::new(etype, data));
|
||||
}
|
||||
|
||||
Ok(Some(Self { extensions }))
|
||||
}
|
||||
}
|
||||
+7
-8
@@ -1,17 +1,17 @@
|
||||
use bytes::{Buf, Bytes};
|
||||
|
||||
use crate::{
|
||||
protocol::interceptors::{
|
||||
error_interceptor::interceptor_error::{ErrorAction, ErrorType, InterceptorError},
|
||||
interceptor::Interceptor,
|
||||
protocol::{
|
||||
interceptors::error_interceptor::{ErrorAction, ErrorType, InterceptorError},
|
||||
parser::parser::FrameParser,
|
||||
},
|
||||
tlseng::tls::{HelloHeader, HelloType},
|
||||
tlseng::{handshake::hello_header::HelloHeader, types::HelloType},
|
||||
utils::u24::{BufExt, U24},
|
||||
};
|
||||
|
||||
impl Interceptor for HelloHeader {
|
||||
impl FrameParser for HelloHeader {
|
||||
type Error = InterceptorError;
|
||||
fn can_handle(bytes: &bytes::BytesMut) -> bool {
|
||||
fn can_parse(bytes: &bytes::BytesMut) -> bool {
|
||||
if bytes.is_empty() {
|
||||
return false;
|
||||
}
|
||||
@@ -23,7 +23,7 @@ impl Interceptor for HelloHeader {
|
||||
is_valid
|
||||
}
|
||||
|
||||
fn intercept(bytes: &mut bytes::BytesMut) -> Result<Option<Self>, Self::Error>
|
||||
fn parse(bytes: &mut bytes::BytesMut) -> Result<Option<Self>, Self::Error>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
@@ -42,7 +42,6 @@ impl Interceptor for HelloHeader {
|
||||
Ok(Some(Self {
|
||||
header_type,
|
||||
len: U24::from_u32(len),
|
||||
body: bytes.split_to(len as usize).freeze(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
mod app_data_parser;
|
||||
mod client_hello_parser;
|
||||
mod extension_parser;
|
||||
mod h_header_parser;
|
||||
mod server_hello_parser;
|
||||
mod tls_header_parser;
|
||||
+10
-10
@@ -1,23 +1,23 @@
|
||||
use bytes::{Buf, Bytes};
|
||||
use bytes::{Buf, Bytes, BytesMut};
|
||||
|
||||
use crate::{
|
||||
protocol::interceptors::{
|
||||
error_interceptor::interceptor_error::{ErrorAction, ErrorType, InterceptorError},
|
||||
interceptor::Interceptor,
|
||||
protocol::{
|
||||
interceptors::error_interceptor::{ErrorAction, ErrorType, InterceptorError},
|
||||
parser::parser::FrameParser,
|
||||
},
|
||||
tlseng::tls::{ProtocolVersion, ServerHello},
|
||||
tlseng::{handshake::server_hello::ServerHello, types::ProtocolVersion},
|
||||
};
|
||||
|
||||
impl Interceptor for ServerHello {
|
||||
impl FrameParser for ServerHello {
|
||||
type Error = InterceptorError;
|
||||
|
||||
fn can_handle(bytes: &bytes::BytesMut) -> bool {
|
||||
fn can_parse(bytes: &bytes::BytesMut) -> bool {
|
||||
// Минимальный ServerHello:
|
||||
// Version(2) + Random(32) + SessionID_len(1) + Cipher(2) + Compression(1) = 38 байт
|
||||
bytes.len() >= 38
|
||||
}
|
||||
|
||||
fn intercept(bytes: &mut bytes::BytesMut) -> Result<Option<Self>, Self::Error>
|
||||
fn parse(bytes: &mut bytes::BytesMut) -> Result<Option<Self>, Self::Error>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
@@ -67,7 +67,7 @@ impl Interceptor for ServerHello {
|
||||
random,
|
||||
session_id,
|
||||
cipher_suite,
|
||||
extensions: Bytes::new(),
|
||||
extensions: BytesMut::new(),
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ impl Interceptor for ServerHello {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let extensions = bytes.split_to(extensions_len).freeze();
|
||||
let extensions = bytes.split_to(extensions_len);
|
||||
|
||||
Ok(Some(Self {
|
||||
version,
|
||||
+11
-13
@@ -1,16 +1,19 @@
|
||||
use bytes::{Buf, Bytes, BytesMut};
|
||||
|
||||
use crate::{
|
||||
protocol::interceptors::{
|
||||
error_interceptor::interceptor_error::{ErrorAction, ErrorType, InterceptorError},
|
||||
interceptor::Interceptor,
|
||||
protocol::{
|
||||
interceptors::error_interceptor::{ErrorAction, ErrorType, InterceptorError},
|
||||
parser::parser::FrameParser,
|
||||
},
|
||||
tlseng::{
|
||||
tls_record::TlsRecord,
|
||||
types::{ContentType, ProtocolVersion},
|
||||
},
|
||||
tlseng::tls::{ContentType, ProtocolVersion, TlsRecord},
|
||||
};
|
||||
|
||||
impl Interceptor for TlsRecord {
|
||||
impl FrameParser for TlsRecord {
|
||||
type Error = InterceptorError;
|
||||
fn can_handle(bytes: &BytesMut) -> bool {
|
||||
fn can_parse(bytes: &BytesMut) -> bool {
|
||||
if bytes.is_empty() {
|
||||
return false;
|
||||
}
|
||||
@@ -22,7 +25,7 @@ impl Interceptor for TlsRecord {
|
||||
};
|
||||
is_valid
|
||||
}
|
||||
fn intercept(bytes: &mut BytesMut) -> Result<Option<TlsRecord>, Self::Error> {
|
||||
fn parse(bytes: &mut BytesMut) -> Result<Option<TlsRecord>, Self::Error> {
|
||||
if bytes.len() < 5 {
|
||||
return Ok(None);
|
||||
}
|
||||
@@ -48,11 +51,6 @@ impl Interceptor for TlsRecord {
|
||||
})?;
|
||||
let _raw_len = bytes.get_u16();
|
||||
let payload = bytes.split_to(len as usize).freeze();
|
||||
Ok(Some(TlsRecord {
|
||||
content_type,
|
||||
version,
|
||||
len,
|
||||
payload,
|
||||
}))
|
||||
Ok(Some(TlsRecord::new(content_type, version, payload)))
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,6 @@ impl BufPair {
|
||||
}
|
||||
|
||||
pub async fn write_to(&mut self, writer: &mut OwnedWriteHalf) -> Result<(), String> {
|
||||
println!("Writer Before write {:?}", self.write_buf);
|
||||
writer
|
||||
.write_all_buf(&mut self.write_buf)
|
||||
.await
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::{net::SocketAddr, sync::Arc, vec};
|
||||
use std::{net::SocketAddr, sync::Arc};
|
||||
use tokio::net::{
|
||||
tcp::{OwnedReadHalf, OwnedWriteHalf},
|
||||
TcpStream,
|
||||
@@ -21,7 +21,7 @@ pub struct Connection {
|
||||
}
|
||||
|
||||
impl Connection {
|
||||
pub fn new(stream: TcpStream, addr: SocketAddr) -> Self {
|
||||
pub fn new(stream: TcpStream, addr: SocketAddr, init: bool) -> Self {
|
||||
let (inbound, outbound) = stream.into_split();
|
||||
Self {
|
||||
addr,
|
||||
@@ -29,7 +29,7 @@ impl Connection {
|
||||
outbound,
|
||||
state: ConnectionState::New,
|
||||
buffers: BufPair::new(),
|
||||
codec: Codec::new(),
|
||||
codec: Codec::new(init),
|
||||
}
|
||||
}
|
||||
pub async fn handle(
|
||||
@@ -40,12 +40,12 @@ impl Connection {
|
||||
match &mut self.state {
|
||||
ConnectionState::New => {
|
||||
self.state = handler
|
||||
.do_new(&mut self.inbound, &mut self.outbound, &mut self.buffers)
|
||||
.init_session(&mut self.inbound, &mut self.outbound, &mut self.buffers)
|
||||
.await?
|
||||
}
|
||||
ConnectionState::Handshake => {
|
||||
self.state = handler
|
||||
.do_handshake(
|
||||
.authorize_request(
|
||||
&mut self.inbound,
|
||||
&mut self.outbound,
|
||||
&mut self.buffers,
|
||||
@@ -55,7 +55,7 @@ impl Connection {
|
||||
}
|
||||
ConnectionState::Tunnel(ref mut stream) => {
|
||||
self.state = handler
|
||||
.do_tunnel(
|
||||
.exchange_data(
|
||||
&mut self.inbound,
|
||||
&mut self.outbound,
|
||||
&mut self.buffers,
|
||||
@@ -67,14 +67,13 @@ impl Connection {
|
||||
|
||||
ConnectionState::Close => {
|
||||
self.state = handler
|
||||
.do_close(&mut self.inbound, &mut self.outbound, &mut self.buffers)
|
||||
.finalize_session(&mut self.inbound, &mut self.outbound, &mut self.buffers)
|
||||
.await?;
|
||||
}
|
||||
ConnectionState::Disconnected => {
|
||||
println!("Disconnected");
|
||||
return Ok(ConnectionState::Disconnected);
|
||||
}
|
||||
_ => return Err("Invalid transition".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,31 +10,31 @@ use crate::{
|
||||
};
|
||||
#[async_trait]
|
||||
pub trait ProxyHandler {
|
||||
async fn do_new(
|
||||
async fn init_session(
|
||||
&self,
|
||||
reader: &mut OwnedReadHalf,
|
||||
writer: &mut OwnedWriteHalf,
|
||||
client_reader: &mut OwnedReadHalf,
|
||||
client_writer: &mut OwnedWriteHalf,
|
||||
buffers: &mut BufPair,
|
||||
) -> Result<ConnectionState, String>;
|
||||
async fn do_handshake(
|
||||
async fn authorize_request(
|
||||
&self,
|
||||
reader: &mut OwnedReadHalf,
|
||||
writer: &mut OwnedWriteHalf,
|
||||
client_reader: &mut OwnedReadHalf,
|
||||
client_writer: &mut OwnedWriteHalf,
|
||||
buffers: &mut BufPair,
|
||||
codec: &mut Codec,
|
||||
) -> Result<ConnectionState, String>;
|
||||
async fn do_tunnel(
|
||||
async fn exchange_data(
|
||||
&self,
|
||||
reader: &mut OwnedReadHalf,
|
||||
writer: &mut OwnedWriteHalf,
|
||||
client_reader: &mut OwnedReadHalf,
|
||||
client_writer: &mut OwnedWriteHalf,
|
||||
buffers: &mut BufPair,
|
||||
codec: &mut Codec,
|
||||
target: &mut TcpStream,
|
||||
) -> Result<ConnectionState, String>;
|
||||
async fn do_close(
|
||||
async fn finalize_session(
|
||||
&self,
|
||||
reader: &mut OwnedReadHalf,
|
||||
writer: &mut OwnedWriteHalf,
|
||||
client_reader: &mut OwnedReadHalf,
|
||||
client_writer: &mut OwnedWriteHalf,
|
||||
buffers: &mut BufPair,
|
||||
) -> Result<ConnectionState, String>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
pub mod handler;
|
||||
pub mod netr2tcp;
|
||||
pub mod socks2netr;
|
||||
pub mod tcp2netr;
|
||||
mod utils;
|
||||
|
||||
@@ -7,6 +7,7 @@ use crate::{
|
||||
},
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use bytes::BytesMut;
|
||||
use tokio::{
|
||||
io::{AsyncReadExt, AsyncWriteExt},
|
||||
net::{
|
||||
@@ -15,86 +16,114 @@ use tokio::{
|
||||
},
|
||||
};
|
||||
|
||||
enum NetrMessages {}
|
||||
|
||||
pub struct Netr2Tcp;
|
||||
|
||||
impl Netr2Tcp {
|
||||
pub fn raw_addr_to_string(raw: &mut BytesMut) -> Result<String, String> {
|
||||
println!("len is {:?}", raw.len());
|
||||
if raw.is_empty() {
|
||||
return Err("Buffer is empty".into());
|
||||
}
|
||||
|
||||
let len = raw[0] as usize;
|
||||
|
||||
if raw.len() < 1 + len + 2 {
|
||||
return Err(format!(
|
||||
"Buffer too short: expected {}, got {}",
|
||||
1 + len + 2,
|
||||
raw.len()
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
let address = String::from_utf8_lossy(&raw[1..1 + len]);
|
||||
let port_start = 1 + len;
|
||||
let port = u16::from_be_bytes([raw[port_start], raw[port_start + 1]]);
|
||||
|
||||
Ok(format!("{}:{}", address, port))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ProxyHandler for Netr2Tcp {
|
||||
//TODO MAKE ANSWER TO CONNECT
|
||||
//CLOSE CONNECT HERE IF IT IS NOT MY PROXY
|
||||
async fn do_new(
|
||||
async fn init_session(
|
||||
&self,
|
||||
reader: &mut OwnedReadHalf,
|
||||
writer: &mut OwnedWriteHalf,
|
||||
buffers: &mut BufPair,
|
||||
_client_reader: &mut OwnedReadHalf,
|
||||
_client_writer: &mut OwnedWriteHalf,
|
||||
_buffers: &mut BufPair,
|
||||
) -> Result<ConnectionState, String> {
|
||||
//buffers.read_from(reader).await?;
|
||||
let codec = Codec::new();
|
||||
Ok(ConnectionState::Handshake)
|
||||
}
|
||||
|
||||
//TODO MAKE HANDSHAKE WITH TARGET
|
||||
//GET TARGET FROM CUSTOM FRAME
|
||||
async fn do_handshake(
|
||||
async fn authorize_request(
|
||||
&self,
|
||||
reader: &mut OwnedReadHalf,
|
||||
writer: &mut OwnedWriteHalf,
|
||||
client_reader: &mut OwnedReadHalf,
|
||||
_client_writer: &mut OwnedWriteHalf,
|
||||
buffers: &mut BufPair,
|
||||
codec: &mut Codec,
|
||||
_codec: &mut Codec,
|
||||
) -> Result<ConnectionState, String> {
|
||||
buffers.read_from(reader).await?;
|
||||
buffers.read_from(client_reader).await?;
|
||||
|
||||
let mut target_stream = TcpStream::connect("google.com:443")
|
||||
let header_len = if !buffers.read_buf.is_empty() {
|
||||
1 + (buffers.read_buf[0] as usize) + 2
|
||||
} else {
|
||||
return Err("Buffer is empty".into());
|
||||
};
|
||||
|
||||
let address_to_connect = Netr2Tcp::raw_addr_to_string(&mut buffers.read_buf)?;
|
||||
println!("Address is: {:?}", address_to_connect);
|
||||
|
||||
// Отрезаем заголовок, оставляя только данные приложения (TLS и т.д.)
|
||||
let _header = buffers.read_buf.split_to(header_len);
|
||||
|
||||
let target_stream = TcpStream::connect(address_to_connect)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
target_stream.write_buf(&mut buffers.read_buf).await;
|
||||
target_stream.read_buf(&mut buffers.write_buf).await;
|
||||
writer.write_buf(&mut buffers.write_buf);
|
||||
buffers.write_to(writer).await?;
|
||||
|
||||
Ok(ConnectionState::Tunnel(target_stream))
|
||||
}
|
||||
|
||||
//TODO TUNNEL TO TARGET
|
||||
async fn do_tunnel(
|
||||
async fn exchange_data(
|
||||
&self,
|
||||
reader: &mut OwnedReadHalf,
|
||||
mut writer: &mut OwnedWriteHalf,
|
||||
client_reader: &mut OwnedReadHalf,
|
||||
client_writer: &mut OwnedWriteHalf,
|
||||
buffers: &mut BufPair,
|
||||
codec: &mut Codec,
|
||||
_codec: &mut Codec,
|
||||
target: &mut TcpStream,
|
||||
) -> Result<ConnectionState, String> {
|
||||
let (mut target_reader, mut target_writer) = target.split();
|
||||
println!("Netr2Tcp Стартанул в тонель");
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
// 1. from client to target
|
||||
res = reader.read_buf(&mut buffers.read_buf) => {
|
||||
// 1. От клиента к целевому серверу
|
||||
res = client_reader.read_buf(&mut buffers.read_buf) => {
|
||||
let should_break =
|
||||
relay_data(res, &mut target_writer, &mut buffers.read_buf).await?;
|
||||
if should_break { break;}
|
||||
if should_break { break; }
|
||||
}
|
||||
|
||||
// 2. From target to client
|
||||
// 2. От целевого сервера к клиенту
|
||||
res = target_reader.read_buf(&mut buffers.write_buf) => {
|
||||
let should_break =
|
||||
relay_data(res, &mut writer, &mut buffers.write_buf).await?;
|
||||
relay_data(res, client_writer, &mut buffers.write_buf).await?;
|
||||
if should_break { break;}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
writer.shutdown().await.map_err(|e| e.to_string())?;
|
||||
|
||||
println!("Cycles breaked");
|
||||
client_writer.shutdown().await.map_err(|e| e.to_string())?;
|
||||
target_writer.shutdown().await.map_err(|e| e.to_string())?;
|
||||
|
||||
Ok(ConnectionState::Close)
|
||||
}
|
||||
|
||||
//CLOSE CONNECT WITH TARGET
|
||||
async fn do_close(
|
||||
async fn finalize_session(
|
||||
&self,
|
||||
reader: &mut OwnedReadHalf,
|
||||
writer: &mut OwnedWriteHalf,
|
||||
buffers: &mut BufPair,
|
||||
_client_reader: &mut OwnedReadHalf,
|
||||
_client_writer: &mut OwnedWriteHalf,
|
||||
_buffers: &mut BufPair,
|
||||
) -> Result<ConnectionState, String> {
|
||||
Ok(ConnectionState::Disconnected)
|
||||
}
|
||||
|
||||
@@ -1,170 +0,0 @@
|
||||
use std::io::Error;
|
||||
|
||||
use crate::{
|
||||
protocol::codec::codec::Codec,
|
||||
proxy::connection::{
|
||||
buf_pair::BufPair,
|
||||
handler::{handler::ProxyHandler, utils::relay_data},
|
||||
state::ConnectionState,
|
||||
},
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use bytes::BufMut;
|
||||
use tokio::{
|
||||
io::{AsyncReadExt, AsyncWriteExt},
|
||||
net::{
|
||||
tcp::{OwnedReadHalf, OwnedWriteHalf},
|
||||
TcpStream,
|
||||
},
|
||||
};
|
||||
|
||||
use bytes::BytesMut;
|
||||
|
||||
enum SocksMsg {
|
||||
Hello, // Hello (0x05, 0x00)
|
||||
AuthFailed, // Error (0x05, 0xFF)
|
||||
ConnectOk, // Connection is OK
|
||||
Custom(Vec<u8>), // If needed raw bytes
|
||||
}
|
||||
|
||||
impl SocksMsg {
|
||||
pub fn write_to(self, buf: &mut BytesMut) {
|
||||
buf.clear(); // Чистим перед записью ВСЕГДА
|
||||
match self {
|
||||
SocksMsg::Hello => {
|
||||
buf.put_slice(&[0x05, 0x00]);
|
||||
}
|
||||
SocksMsg::AuthFailed => {
|
||||
buf.put_slice(&[0x05, 0xFF]);
|
||||
}
|
||||
SocksMsg::ConnectOk => {
|
||||
// SOCKS5 требует 10 байт в ответ на CONNECT:
|
||||
// VER, REP(0), RSV, ATYP(1), ADDR(0,0,0,0), PORT(0,0)
|
||||
buf.put_slice(&[0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0]);
|
||||
}
|
||||
SocksMsg::Custom(data) => {
|
||||
buf.put_slice(&data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Socks2Netr;
|
||||
|
||||
impl Socks2Netr {
|
||||
fn get_addr(data: &BytesMut) -> Result<String, String> {
|
||||
let target_addr = match data[3] {
|
||||
0x01 => {
|
||||
// IPv4 (4 байта IP + 2 байта порт)
|
||||
let ip = format!("{}.{}.{}.{}", data[4], data[5], data[6], data[7]);
|
||||
let port = u16::from_be_bytes([data[8], data[9]]);
|
||||
format!("{}:{}", ip, port)
|
||||
}
|
||||
0x03 => {
|
||||
// Domain Name
|
||||
let len = data[4] as usize;
|
||||
let domain = String::from_utf8_lossy(&data[5..5 + len]);
|
||||
let port = u16::from_be_bytes([data[5 + len], data[5 + len + 1]]);
|
||||
format!("{}:{}", domain, port)
|
||||
}
|
||||
_ => return Err("Unsupported address type".to_string()),
|
||||
};
|
||||
Ok(target_addr)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ProxyHandler for Socks2Netr {
|
||||
//get tcp from socks connection. give answer to it
|
||||
async fn do_new(
|
||||
&self,
|
||||
reader: &mut OwnedReadHalf,
|
||||
writer: &mut OwnedWriteHalf,
|
||||
buffers: &mut BufPair,
|
||||
) -> Result<ConnectionState, String> {
|
||||
buffers.read_from(reader).await?;
|
||||
SocksMsg::Hello.write_to(&mut buffers.write_buf);
|
||||
buffers.write_to(writer).await?;
|
||||
|
||||
let codec = Codec::new();
|
||||
|
||||
Ok(ConnectionState::Handshake)
|
||||
}
|
||||
|
||||
async fn do_handshake(
|
||||
&self,
|
||||
reader: &mut OwnedReadHalf,
|
||||
writer: &mut OwnedWriteHalf,
|
||||
buffers: &mut BufPair,
|
||||
codec: &mut Codec,
|
||||
) -> Result<ConnectionState, String> {
|
||||
buffers.read_from(reader).await?;
|
||||
let data = &buffers.read_buf;
|
||||
|
||||
if data.len() < 7 {
|
||||
return Err("SOCKS5 request too short".to_string());
|
||||
}
|
||||
|
||||
//CODEC HERE SHOULD CREATE CUSTOM FRAME
|
||||
//AND MAKE MOCK TLS HANDSHAKE WITH MY SERVER
|
||||
//AFTER THIS SEND THE REAL HANDSHAKE BUT IN APPLICATION DATA
|
||||
|
||||
let target_addr = Socks2Netr::get_addr(data)?;
|
||||
println!("Connecting to target: {}", target_addr);
|
||||
//todo dynamic address of proxy server
|
||||
let endpoint_stream = TcpStream::connect("127.0.0.1:4443")
|
||||
.await
|
||||
.map_err(|e| format!("Could not connect to {}: {}", target_addr, e))?;
|
||||
|
||||
SocksMsg::ConnectOk.write_to(&mut buffers.write_buf);
|
||||
buffers.write_to(writer).await?;
|
||||
Ok(ConnectionState::Tunnel(endpoint_stream))
|
||||
}
|
||||
|
||||
//their tunnel. there is encrypt tcp to netr
|
||||
async fn do_tunnel(
|
||||
&self,
|
||||
reader: &mut OwnedReadHalf,
|
||||
mut writer: &mut OwnedWriteHalf,
|
||||
buffers: &mut BufPair,
|
||||
codec: &mut Codec,
|
||||
target: &mut TcpStream,
|
||||
) -> Result<ConnectionState, String> {
|
||||
buffers.reset();
|
||||
let (mut target_reader, mut target_writer) = target.split();
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
// 1. from client to target
|
||||
res = reader.read_buf(&mut buffers.read_buf) => {
|
||||
let should_break =
|
||||
relay_data(res, &mut target_writer, &mut buffers.read_buf).await?;
|
||||
if should_break { break;}
|
||||
}
|
||||
|
||||
// 2. From target to client
|
||||
res = target_reader.read_buf(&mut buffers.write_buf) => {
|
||||
let should_break =
|
||||
relay_data(res, &mut writer, &mut buffers.write_buf).await?;
|
||||
if should_break { break;}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
writer.shutdown().await.map_err(|e| e.to_string())?;
|
||||
target_writer.shutdown().await.map_err(|e| e.to_string())?;
|
||||
|
||||
Ok(ConnectionState::Close)
|
||||
}
|
||||
|
||||
//close tunnel between mobile (tun2socks) and server
|
||||
async fn do_close(
|
||||
&self,
|
||||
_reader: &mut OwnedReadHalf,
|
||||
_writer: &mut OwnedWriteHalf,
|
||||
_buffers: &mut BufPair,
|
||||
) -> Result<ConnectionState, String> {
|
||||
println!("SOCKS5 CONNECTUON CLOSED");
|
||||
Ok(ConnectionState::Disconnected)
|
||||
}
|
||||
}
|
||||
@@ -1,60 +1,208 @@
|
||||
use crate::{
|
||||
protocol::codec::codec::Codec,
|
||||
proxy::connection::{
|
||||
buf_pair::BufPair, handler::handler::ProxyHandler, state::ConnectionState,
|
||||
buf_pair::BufPair,
|
||||
handler::{handler::ProxyHandler, utils::relay_data},
|
||||
state::ConnectionState,
|
||||
},
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use tokio::net::{
|
||||
tcp::{OwnedReadHalf, OwnedWriteHalf},
|
||||
TcpStream,
|
||||
use bytes::{BufMut, Bytes};
|
||||
use tokio::{
|
||||
io::{AsyncReadExt, AsyncWriteExt},
|
||||
net::{
|
||||
tcp::{OwnedReadHalf, OwnedWriteHalf},
|
||||
TcpStream,
|
||||
},
|
||||
};
|
||||
|
||||
pub struct Tcp2Netr;
|
||||
use bytes::BytesMut;
|
||||
|
||||
enum SocksMsg {
|
||||
Hello, // Hello (0x05, 0x00)
|
||||
AuthFailed, // Error (0x05, 0xFF)
|
||||
ConnectOk, // Connection is OK
|
||||
Custom(Vec<u8>), // If needed raw bytes
|
||||
}
|
||||
|
||||
impl SocksMsg {
|
||||
pub fn write_to(self, buf: &mut BytesMut) {
|
||||
buf.clear(); // Чистим перед записью ВСЕГДА
|
||||
match self {
|
||||
SocksMsg::Hello => {
|
||||
buf.put_slice(&[0x05, 0x00]);
|
||||
}
|
||||
SocksMsg::AuthFailed => {
|
||||
buf.put_slice(&[0x05, 0xFF]);
|
||||
}
|
||||
SocksMsg::ConnectOk => {
|
||||
// SOCKS5 требует 10 байт в ответ на CONNECT:
|
||||
// VER, REP(0), RSV, ATYP(1), ADDR(0,0,0,0), PORT(0,0)
|
||||
buf.put_slice(&[0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0]);
|
||||
}
|
||||
SocksMsg::Custom(data) => {
|
||||
buf.put_slice(&data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Tcp2Netr {
|
||||
socks5: bool,
|
||||
proxy_address: String,
|
||||
}
|
||||
|
||||
impl Tcp2Netr {
|
||||
pub fn new(socks5: bool, address: String) -> Self {
|
||||
Self {
|
||||
socks5,
|
||||
proxy_address: address,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_addr_raw(data: &mut BytesMut) -> Result<Bytes, String> {
|
||||
if data.len() < 4 {
|
||||
return Err("Too short".into());
|
||||
}
|
||||
|
||||
let atyp = data[3];
|
||||
let total_len = match atyp {
|
||||
0x01 => 10, // 4 (header) + 4 (ip) + 2 (port)
|
||||
0x03 => {
|
||||
let domain_len = data[4] as usize;
|
||||
4 + 1 + domain_len + 2 // 4 (header) + 1 (len) + N (domain) + 2 (port)
|
||||
}
|
||||
_ => return Err("Unsupported address type".to_string()),
|
||||
};
|
||||
|
||||
if data.len() < total_len {
|
||||
return Err("Incomplete SOCKS packet".into());
|
||||
}
|
||||
|
||||
// ВАЖНО: split_to удаляет эти байты из data и возвращает их нам
|
||||
// Теперь в data останется только TLS ClientHello!
|
||||
let socks_packet = data.split_to(total_len);
|
||||
|
||||
// Формируем твой кастомный адрес (длина + данные + порт)
|
||||
let mut result = BytesMut::new();
|
||||
if atyp == 0x01 {
|
||||
result.put_u8(4);
|
||||
result.put_slice(&socks_packet[4..10]);
|
||||
} else {
|
||||
let len = socks_packet[4];
|
||||
result.put_u8(len);
|
||||
result.put_slice(&socks_packet[5..total_len]);
|
||||
}
|
||||
|
||||
Ok(result.freeze())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ProxyHandler for Tcp2Netr {
|
||||
//for new tunnel between site.ru and proxy
|
||||
async fn do_new(
|
||||
// 1. Инициализация: отвечаем SOCKS5 Hello (0x05, 0x00)
|
||||
async fn init_session(
|
||||
&self,
|
||||
reader: &mut OwnedReadHalf,
|
||||
writer: &mut OwnedWriteHalf,
|
||||
client_reader: &mut OwnedReadHalf,
|
||||
client_writer: &mut OwnedWriteHalf,
|
||||
buffers: &mut BufPair,
|
||||
) -> Result<ConnectionState, String> {
|
||||
todo!()
|
||||
//Ok(ConnectionState::Handshake)
|
||||
buffers.read_from(client_reader).await?;
|
||||
|
||||
SocksMsg::Hello.write_to(&mut buffers.write_buf);
|
||||
buffers.write_to(client_writer).await?;
|
||||
|
||||
Ok(ConnectionState::Handshake)
|
||||
}
|
||||
|
||||
//their handshake. need to encrypt this in netr protocol
|
||||
async fn do_handshake(
|
||||
// 2. Авторизация/Парсинг: достаем адрес из SOCKS5 Connect и подключаемся к Netr
|
||||
async fn authorize_request(
|
||||
&self,
|
||||
reader: &mut OwnedReadHalf,
|
||||
writer: &mut OwnedWriteHalf,
|
||||
client_reader: &mut OwnedReadHalf,
|
||||
client_writer: &mut OwnedWriteHalf,
|
||||
buffers: &mut BufPair,
|
||||
codec: &mut Codec,
|
||||
_codec: &mut Codec,
|
||||
) -> Result<ConnectionState, String> {
|
||||
Ok(ConnectionState::Close)
|
||||
buffers.read_from(client_reader).await?;
|
||||
|
||||
// Извлекаем адрес и удаляем SOCKS-заголовок из буфера
|
||||
let address_to_connect = Tcp2Netr::get_addr_raw(&mut buffers.read_buf)?;
|
||||
|
||||
// Коннектимся к твоему Netr серверу (proxy_address)
|
||||
let mut target_stream = TcpStream::connect(&self.proxy_address)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
// Шлем кастомный заголовок адреса в сторону Netr
|
||||
target_stream
|
||||
.write_all(&address_to_connect)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
// Если в буфере остался TLS ClientHello (после split_to в get_addr_raw),
|
||||
// проталкиваем его немедленно, чтобы сервер не ждал
|
||||
if !buffers.read_buf.is_empty() {
|
||||
target_stream
|
||||
.write_all(&buffers.read_buf)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
buffers.read_buf.clear();
|
||||
}
|
||||
|
||||
// Отвечаем клиенту (браузеру), что SOCKS-соединение установлено
|
||||
SocksMsg::ConnectOk.write_to(&mut buffers.write_buf);
|
||||
buffers.write_to(client_writer).await?;
|
||||
|
||||
// Переходим в режим туннеля, передавая сокет до Netr-сервера
|
||||
Ok(ConnectionState::Tunnel(target_stream))
|
||||
}
|
||||
|
||||
//data flow like application data in netr protocol
|
||||
async fn do_tunnel(
|
||||
// 3. Обмен данными: гоняем байты между клиентом и Netr-сервером
|
||||
async fn exchange_data(
|
||||
&self,
|
||||
reader: &mut OwnedReadHalf,
|
||||
writer: &mut OwnedWriteHalf,
|
||||
client_reader: &mut OwnedReadHalf,
|
||||
client_writer: &mut OwnedWriteHalf,
|
||||
buffers: &mut BufPair,
|
||||
codec: &mut Codec,
|
||||
_codec: &mut Codec,
|
||||
target: &mut TcpStream,
|
||||
) -> Result<ConnectionState, String> {
|
||||
buffers.reset();
|
||||
let (mut target_reader, mut target_writer) = target.split();
|
||||
|
||||
println!("Socks2Netr Стартанул в тонель");
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
// Из браузера -> в сторону Netr
|
||||
res = client_reader.read_buf(&mut buffers.read_buf) => {
|
||||
let should_break =
|
||||
relay_data(res, &mut target_writer, &mut buffers.read_buf).await?;
|
||||
if should_break { break;}
|
||||
}
|
||||
|
||||
// Из Netr -> обратно в браузер
|
||||
res = target_reader.read_buf(&mut buffers.write_buf) => {
|
||||
let should_break =
|
||||
relay_data(res, client_writer, &mut buffers.write_buf).await?;
|
||||
if should_break { break;}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client_writer.shutdown().await.map_err(|e| e.to_string())?;
|
||||
target_writer.shutdown().await.map_err(|e| e.to_string())?;
|
||||
|
||||
Ok(ConnectionState::Close)
|
||||
}
|
||||
|
||||
//close of connection
|
||||
async fn do_close(
|
||||
// 4. Финализация: логируем закрытие
|
||||
async fn finalize_session(
|
||||
&self,
|
||||
reader: &mut OwnedReadHalf,
|
||||
writer: &mut OwnedWriteHalf,
|
||||
buffers: &mut BufPair,
|
||||
_client_reader: &mut OwnedReadHalf,
|
||||
_client_writer: &mut OwnedWriteHalf,
|
||||
_buffers: &mut BufPair,
|
||||
) -> Result<ConnectionState, String> {
|
||||
Ok(ConnectionState::Close)
|
||||
println!("SOCKS5 CONNECTION CLOSED");
|
||||
Ok(ConnectionState::Disconnected)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,13 +11,16 @@ pub async fn relay_data<W>(
|
||||
where
|
||||
W: AsyncWriteExt + Unpin,
|
||||
{
|
||||
let n = res.map_err(|e| e.to_string())?;
|
||||
if n == 0 {
|
||||
return Ok(true);
|
||||
match res {
|
||||
Ok(0) => {
|
||||
println!("Read 0 bytes - closing half of connection");
|
||||
return Ok(true); // Это закроет туннель
|
||||
}
|
||||
Ok(n) => println!("Relayed {} bytes", n),
|
||||
Err(e) => println!("Relay error: {}", e),
|
||||
}
|
||||
|
||||
println!(">>> Client sent {} bytes", n);
|
||||
|
||||
writer.write_all(&buffer).await.map_err(|e| e.to_string())?;
|
||||
println!("What is here {:?}", &buffer);
|
||||
writer.write_buf(buffer).await.map_err(|e| e.to_string())?;
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
use tokio::net::TcpStream;
|
||||
|
||||
use crate::protocol::codec::codec::Codec;
|
||||
|
||||
//todo split to codec that uses my frame and tls codec that remove camouflage
|
||||
pub enum ConnectionState {
|
||||
New,
|
||||
|
||||
@@ -34,7 +34,7 @@ impl Network {
|
||||
let (stream, addr) = listener.accept().await.expect("Error on connection");
|
||||
let handler = self.inbound_handler.clone();
|
||||
tokio::spawn(async move {
|
||||
let mut conection: Connection = Connection::new(stream, addr);
|
||||
let mut conection: Connection = Connection::new(stream, addr, false);
|
||||
|
||||
let res = conection.handle(handler).await;
|
||||
match res {
|
||||
@@ -45,7 +45,7 @@ impl Network {
|
||||
ConnectionState::Handshake => {
|
||||
println!("Connection {} handshaked", addr)
|
||||
}
|
||||
ConnectionState::Tunnel(stream) => {
|
||||
ConnectionState::Tunnel(_stream) => {
|
||||
println!("Connection {} tunnel", addr)
|
||||
}
|
||||
ConnectionState::Close => {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
use bytes::{Bytes, BytesMut};
|
||||
|
||||
use crate::tlseng::{
|
||||
tls_record::TlsRecord,
|
||||
types::{ContentType, ProtocolVersion},
|
||||
};
|
||||
|
||||
pub struct ApplicationData {
|
||||
pub len: usize,
|
||||
pub payload: Bytes,
|
||||
}
|
||||
|
||||
impl ApplicationData {
|
||||
pub fn make_application_data(bytes: &mut BytesMut) -> Bytes {
|
||||
let record = TlsRecord::new(
|
||||
ContentType::ApplicationData,
|
||||
ProtocolVersion::Tls12,
|
||||
bytes.split_to(bytes.len()).freeze(),
|
||||
);
|
||||
record.serialize()
|
||||
}
|
||||
}
|
||||
@@ -3,18 +3,24 @@ use rand::Rng;
|
||||
|
||||
// Using your provided constants and types
|
||||
use crate::tlseng::{
|
||||
etype::*,
|
||||
consts::*,
|
||||
params::{TlsGroups, TlsSignatures, TlsVersions},
|
||||
profile::profile::BrowserProfile,
|
||||
values::*,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Extension {
|
||||
pub etype: u16,
|
||||
pub elen: u16,
|
||||
pub data: Bytes,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ExtensionStack {
|
||||
pub extensions: Vec<Extension>,
|
||||
}
|
||||
|
||||
impl Extension {
|
||||
pub fn new(etype: u16, data: Bytes) -> Self {
|
||||
Self {
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
use bytes::{BufMut, Bytes, BytesMut};
|
||||
|
||||
use crate::{
|
||||
crypto::hmac::generate_auth_tag,
|
||||
tlseng::{
|
||||
consts::HANDSHAKE_TYPE_CLIENT_HELLO,
|
||||
extension::ExtensionBuilder,
|
||||
profile::profile::BrowserProfile,
|
||||
tls_record::TlsRecord,
|
||||
types::{ContentType, ProtocolVersion},
|
||||
},
|
||||
};
|
||||
|
||||
pub struct ClientHello {
|
||||
/// The maximum version supported (legacy field in TLS 1.3)
|
||||
pub version: ProtocolVersion,
|
||||
/// 32 bytes of client-generated entropy
|
||||
pub random: [u8; 32],
|
||||
/// Legacy session ID (used in TLS 1.3 for middlebox compatibility)
|
||||
pub session_id: Bytes,
|
||||
/// List of cryptographic ciphers the client supports
|
||||
pub cipher_suites: Vec<u16>,
|
||||
/// Opaque block of extensions generated by ExtensionBuilder
|
||||
pub extensions: Bytes,
|
||||
}
|
||||
|
||||
impl ClientHello {
|
||||
/// Serializes the ClientHello message into its wire format.
|
||||
/// Includes the 4-byte Handshake header (Type + Length).
|
||||
pub fn serialize(&self) -> Bytes {
|
||||
let mut buf = BytesMut::with_capacity(512 + self.extensions.len());
|
||||
|
||||
// Handshake Type: 0x01 (ClientHello)
|
||||
buf.put_u8(HANDSHAKE_TYPE_CLIENT_HELLO);
|
||||
|
||||
// Handshake Length Placeholder:
|
||||
// Handshake messages use a 24-bit (3 byte) length field.
|
||||
let length_pos = buf.len();
|
||||
buf.put_bytes(0, 3);
|
||||
|
||||
// Protocol Version:
|
||||
// For TLS 1.3, this is traditionally pinned to 0x0303 (TLS 1.2)
|
||||
// to prevent middleboxes from dropping the packet.
|
||||
buf.put_u16(0x0303);
|
||||
buf.put_slice(&self.random);
|
||||
|
||||
// Legacy Session ID:
|
||||
// Formatted as Length (1 byte) + ID bytes.
|
||||
buf.put_u8(self.session_id.len() as u8);
|
||||
buf.put_slice(&self.session_id);
|
||||
|
||||
// Cipher Suites:
|
||||
// Formatted as Total Length (2 bytes) + Suite IDs (2 bytes each).
|
||||
buf.put_u16((self.cipher_suites.len() * 2) as u16);
|
||||
for &suite in &self.cipher_suites {
|
||||
buf.put_u16(suite);
|
||||
}
|
||||
|
||||
// Legacy Compression Methods:
|
||||
// Always 1 byte of length (1) followed by the 'Null' method (0x00).
|
||||
buf.put_u8(1);
|
||||
buf.put_u8(0x00);
|
||||
|
||||
// Extensions Block:
|
||||
// Formatted as Total Length (2 bytes) + Extension Data.
|
||||
buf.put_u16(self.extensions.len() as u16);
|
||||
buf.put_slice(&self.extensions);
|
||||
|
||||
// Patch the Handshake Length:
|
||||
// We calculate the length of everything after the 3-byte placeholder.
|
||||
let total_len = (buf.len() - length_pos - 3) as u32;
|
||||
let len_bytes = total_len.to_be_bytes();
|
||||
// Copy the last 3 bytes of the big-endian u32 into the placeholder.
|
||||
buf[length_pos..length_pos + 3].copy_from_slice(&len_bytes[1..4]);
|
||||
|
||||
buf.freeze()
|
||||
}
|
||||
|
||||
pub fn make_client_hello(profile: &BrowserProfile, host: &str) -> Bytes {
|
||||
// 1. Key Exchange: Generate ECDH pair and get public key
|
||||
|
||||
// 2. Authentication: Generate 32 bytes for TLS Random
|
||||
// [16 bytes entropy] + [16 bytes HMAC(timestamp)]
|
||||
let mut tls_random = [0; 32];
|
||||
|
||||
let auth_token = generate_auth_tag(&[]);
|
||||
tls_random[16..32].copy_from_slice(&auth_token);
|
||||
|
||||
// 3. Extensions: Build the extensions block using the profile
|
||||
let mut ext_builder = ExtensionBuilder::new();
|
||||
// Pass the public key into the KeyShare extension via apply_profile
|
||||
ext_builder.apply_profile(profile, host, &[0; 32]);
|
||||
let extensions_bytes = ext_builder.build();
|
||||
|
||||
let mut session_id = BytesMut::with_capacity(32);
|
||||
session_id.put_slice(&[0u8; 32]);
|
||||
|
||||
// 4. Assemble ClientHello Handshake message
|
||||
let client_hello = ClientHello {
|
||||
version: ProtocolVersion::Tls12, // Legacy version for compatibility
|
||||
random: tls_random,
|
||||
session_id: session_id.freeze(), // Standard 32-byte session ID
|
||||
cipher_suites: vec![0x1301, 0x1302, 0x1303], // TLS 1.3 suites
|
||||
extensions: extensions_bytes,
|
||||
};
|
||||
|
||||
// 5. Wrap ClientHello into a TLS Record
|
||||
let record = TlsRecord::new(
|
||||
ContentType::Handshake,
|
||||
ProtocolVersion::Tls10,
|
||||
client_hello.serialize(),
|
||||
);
|
||||
|
||||
// Final result: Byte buffer ready for the wire
|
||||
record.serialize()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use crate::{tlseng::types::HelloType, utils::u24::U24};
|
||||
|
||||
pub struct HelloHeader {
|
||||
pub header_type: HelloType,
|
||||
pub len: U24,
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
pub mod client_hello;
|
||||
pub mod hello_header;
|
||||
pub mod server_hello;
|
||||
@@ -0,0 +1,95 @@
|
||||
use bytes::{BufMut, Bytes, BytesMut};
|
||||
|
||||
use crate::tlseng::{
|
||||
consts::HANDSHAKE_TYPE_SERVER_HELLO,
|
||||
tls_record::TlsRecord,
|
||||
types::{ContentType, ProtocolVersion},
|
||||
};
|
||||
|
||||
pub struct ServerHello {
|
||||
pub version: ProtocolVersion,
|
||||
pub random: [u8; 32],
|
||||
pub session_id: Bytes,
|
||||
pub cipher_suite: u16,
|
||||
pub extensions: BytesMut,
|
||||
}
|
||||
|
||||
impl ServerHello {
|
||||
pub fn make_mock_server_hello() -> Bytes {
|
||||
// 1. Генерируем "рандом" (в реальном Nginx здесь случайные байты)
|
||||
let mut mock_random = [0u8; 32];
|
||||
mock_random[0..4].copy_from_slice(&[0xDE, 0xAD, 0xBE, 0xEF]); // Просто метка
|
||||
|
||||
// 2. Имитируем Session ID (в TLS 1.3 сервер часто эхоит ID клиента)
|
||||
let mut session_id = BytesMut::with_capacity(32);
|
||||
session_id.put_slice(&[0u8; 32]);
|
||||
|
||||
// 3. Подготавливаем минимальные расширения (пустые или базовые)
|
||||
// Для TLS 1.3 тут обязательно должны быть Supported Versions (0x002b)
|
||||
let mut mock_extensions = BytesMut::new();
|
||||
|
||||
// Extension: Supported Versions (TLS 1.3)
|
||||
mock_extensions.put_u16(0x002b); // Type
|
||||
mock_extensions.put_u16(2); // Length
|
||||
mock_extensions.put_u16(0x0304); // Value: TLS 1.3
|
||||
|
||||
let server_hello = ServerHello {
|
||||
version: ProtocolVersion::Tls12, // Legacy 0x0303
|
||||
random: mock_random,
|
||||
session_id: session_id.freeze(),
|
||||
cipher_suite: 0x1301, // TLS_AES_128_GCM_SHA256
|
||||
extensions: mock_extensions,
|
||||
};
|
||||
|
||||
// 4. Сериализуем Handshake сообщение
|
||||
let handshake_payload = server_hello.serialize();
|
||||
|
||||
// 5. Оборачиваем в TLS Record
|
||||
// ContentType: Handshake (22)
|
||||
// Version: TLS 1.0 (0x0301) для совместимости
|
||||
let record = TlsRecord::new(
|
||||
ContentType::Handshake,
|
||||
ProtocolVersion::Tls10,
|
||||
handshake_payload.freeze(), // Теперь payload — это Bytes
|
||||
);
|
||||
|
||||
// Финальный результат: [Header(5 bytes)][Handshake(N bytes)]
|
||||
record.serialize()
|
||||
}
|
||||
|
||||
pub fn serialize(&self) -> BytesMut {
|
||||
let mut buf = BytesMut::with_capacity(256 + self.extensions.len());
|
||||
|
||||
// 1. Handshake Type: 0x02 (ServerHello)
|
||||
buf.put_u8(HANDSHAKE_TYPE_SERVER_HELLO);
|
||||
|
||||
// 2. Placeholder for u24 length
|
||||
let length_pos = buf.len();
|
||||
buf.put_bytes(0, 3);
|
||||
|
||||
// 3. body of ServerHello
|
||||
buf.put_u16(ProtocolVersion::Tls12 as u16); // Legacy 0x0303
|
||||
buf.put_slice(&self.random);
|
||||
|
||||
// Session ID
|
||||
buf.put_u8(self.session_id.len() as u8);
|
||||
buf.put_slice(&self.session_id);
|
||||
|
||||
// Selected Cipher Suite (only one)
|
||||
buf.put_u16(self.cipher_suite);
|
||||
|
||||
// Compression: always 0x00
|
||||
buf.put_u8(0x00);
|
||||
|
||||
// Extensions
|
||||
buf.put_u16(self.extensions.len() as u16);
|
||||
buf.put_slice(&self.extensions);
|
||||
|
||||
// 4. Patch length
|
||||
let total_len = (buf.len() - length_pos - 3) as u32;
|
||||
let len_bytes = total_len.to_be_bytes();
|
||||
buf[length_pos..length_pos + 3].copy_from_slice(&len_bytes[1..4]);
|
||||
|
||||
buf
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
pub mod etype;
|
||||
pub mod application_data;
|
||||
pub mod consts;
|
||||
pub mod extension;
|
||||
pub mod handshake;
|
||||
mod params;
|
||||
pub mod profile;
|
||||
pub mod tls;
|
||||
pub mod tls_record;
|
||||
pub mod types;
|
||||
mod values;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::tlseng::etype::*;
|
||||
use crate::tlseng::consts::*;
|
||||
use crate::tlseng::params::{TlsGroups, TlsSignatures};
|
||||
use crate::tlseng::values::{
|
||||
GROUP_SECP256R1, GROUP_SECP384R1, GROUP_X25519, SIG_ECDSA_SECP256R1_SHA256,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::tlseng::etype::*;
|
||||
use crate::tlseng::consts::*;
|
||||
use crate::tlseng::params::{TlsGroups, TlsSignatures};
|
||||
use crate::tlseng::values::{
|
||||
GROUP_SECP256R1, GROUP_SECP384R1, GROUP_X25519, SIG_ECDSA_SECP256R1_SHA256,
|
||||
|
||||
@@ -1,232 +0,0 @@
|
||||
use bytes::{BufMut, Bytes, BytesMut};
|
||||
|
||||
use crate::{
|
||||
tlseng::etype::{HANDSHAKE_TYPE_CLIENT_HELLO, HANDSHAKE_TYPE_SERVER_HELLO},
|
||||
utils::u24::U24,
|
||||
};
|
||||
|
||||
/// TLS Content Types as defined in the TLS Record Protocol.
|
||||
/// These identify what is contained within the TLS Record payload.
|
||||
#[repr(u8)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum ContentType {
|
||||
/// Handshake messages (e.g., ClientHello, ServerHello)
|
||||
Handshake = 0x16,
|
||||
/// Encrypted application data (the actual traffic)
|
||||
ApplicationData = 0x17,
|
||||
/// Notification messages (e.g., CloseNotify or error signals)
|
||||
Alert = 0x15,
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for ContentType {
|
||||
type Error = &'static str;
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
0x16 => Ok(ContentType::Handshake),
|
||||
0x17 => Ok(ContentType::ApplicationData),
|
||||
0x15 => Ok(ContentType::Alert),
|
||||
_ => Err("This is not ContentType"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Known TLS protocol versions.
|
||||
/// Note: TLS 1.3 often uses legacy versions in headers for compatibility.
|
||||
#[repr(u16)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum ProtocolVersion {
|
||||
Tls10 = 0x0301,
|
||||
Tls12 = 0x0303,
|
||||
Tls13 = 0x0304,
|
||||
}
|
||||
|
||||
impl TryFrom<u16> for ProtocolVersion {
|
||||
type Error = &'static str;
|
||||
fn try_from(value: u16) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
0x0301 => Ok(ProtocolVersion::Tls10),
|
||||
0x0303 => Ok(ProtocolVersion::Tls12),
|
||||
0x0304 => Ok(ProtocolVersion::Tls13),
|
||||
_ => Err("This is not Protocol Version"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ApplicationData {
|
||||
pub length: usize,
|
||||
pub payload: Bytes,
|
||||
}
|
||||
|
||||
pub enum HelloType {
|
||||
Client = 0x00,
|
||||
Server = 0x01,
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for HelloType {
|
||||
type Error = &'static str;
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
0x00 => Ok(HelloType::Client),
|
||||
0x01 => Ok(HelloType::Server),
|
||||
_ => Err("This is not Hello header"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HelloHeader {
|
||||
pub header_type: HelloType,
|
||||
pub len: U24,
|
||||
pub body: Bytes,
|
||||
}
|
||||
|
||||
/// Represents the ClientHello Handshake message.
|
||||
/// This is the first message sent by a client to initiate a TLS connection.
|
||||
pub struct ClientHello {
|
||||
/// The maximum version supported (legacy field in TLS 1.3)
|
||||
pub version: ProtocolVersion,
|
||||
/// 32 bytes of client-generated entropy
|
||||
pub random: [u8; 32],
|
||||
/// Legacy session ID (used in TLS 1.3 for middlebox compatibility)
|
||||
pub session_id: Bytes,
|
||||
/// List of cryptographic ciphers the client supports
|
||||
pub cipher_suites: Vec<u16>,
|
||||
/// Opaque block of extensions generated by ExtensionBuilder
|
||||
pub extensions: Bytes,
|
||||
}
|
||||
|
||||
impl ClientHello {
|
||||
/// Serializes the ClientHello message into its wire format.
|
||||
/// Includes the 4-byte Handshake header (Type + Length).
|
||||
pub fn serialize(&self) -> Bytes {
|
||||
let mut buf = BytesMut::with_capacity(512 + self.extensions.len());
|
||||
|
||||
// Handshake Type: 0x01 (ClientHello)
|
||||
buf.put_u8(HANDSHAKE_TYPE_CLIENT_HELLO);
|
||||
|
||||
// Handshake Length Placeholder:
|
||||
// Handshake messages use a 24-bit (3 byte) length field.
|
||||
let length_pos = buf.len();
|
||||
buf.put_bytes(0, 3);
|
||||
|
||||
// Protocol Version:
|
||||
// For TLS 1.3, this is traditionally pinned to 0x0303 (TLS 1.2)
|
||||
// to prevent middleboxes from dropping the packet.
|
||||
buf.put_u16(0x0303);
|
||||
buf.put_slice(&self.random);
|
||||
|
||||
// Legacy Session ID:
|
||||
// Formatted as Length (1 byte) + ID bytes.
|
||||
buf.put_u8(self.session_id.len() as u8);
|
||||
buf.put_slice(&self.session_id);
|
||||
|
||||
// Cipher Suites:
|
||||
// Formatted as Total Length (2 bytes) + Suite IDs (2 bytes each).
|
||||
buf.put_u16((self.cipher_suites.len() * 2) as u16);
|
||||
for &suite in &self.cipher_suites {
|
||||
buf.put_u16(suite);
|
||||
}
|
||||
|
||||
// Legacy Compression Methods:
|
||||
// Always 1 byte of length (1) followed by the 'Null' method (0x00).
|
||||
buf.put_u8(1);
|
||||
buf.put_u8(0x00);
|
||||
|
||||
// Extensions Block:
|
||||
// Formatted as Total Length (2 bytes) + Extension Data.
|
||||
buf.put_u16(self.extensions.len() as u16);
|
||||
buf.put_slice(&self.extensions);
|
||||
|
||||
// Patch the Handshake Length:
|
||||
// We calculate the length of everything after the 3-byte placeholder.
|
||||
let total_len = (buf.len() - length_pos - 3) as u32;
|
||||
let len_bytes = total_len.to_be_bytes();
|
||||
// Copy the last 3 bytes of the big-endian u32 into the placeholder.
|
||||
buf[length_pos..length_pos + 3].copy_from_slice(&len_bytes[1..4]);
|
||||
|
||||
buf.freeze()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ServerHello {
|
||||
pub version: ProtocolVersion,
|
||||
pub random: [u8; 32],
|
||||
pub session_id: Bytes,
|
||||
pub cipher_suite: u16,
|
||||
pub extensions: Bytes,
|
||||
}
|
||||
|
||||
impl ServerHello {
|
||||
pub fn serialize(&self) -> Bytes {
|
||||
let mut buf = BytesMut::with_capacity(256 + self.extensions.len());
|
||||
|
||||
// 1. Handshake Type: 0x02 (ServerHello)
|
||||
buf.put_u8(HANDSHAKE_TYPE_SERVER_HELLO);
|
||||
|
||||
// 2. Placeholder для u24 длины
|
||||
let length_pos = buf.len();
|
||||
buf.put_bytes(0, 3);
|
||||
|
||||
// 3. Тело ServerHello
|
||||
buf.put_u16(ProtocolVersion::Tls12 as u16); // Legacy 0x0303
|
||||
buf.put_slice(&self.random);
|
||||
|
||||
// Session ID
|
||||
buf.put_u8(self.session_id.len() as u8);
|
||||
buf.put_slice(&self.session_id);
|
||||
|
||||
// Выбранный Cipher Suite (в отличие от клиента, тут только ОДИН)
|
||||
buf.put_u16(self.cipher_suite);
|
||||
|
||||
// Compression: всегда 0x00
|
||||
buf.put_u8(0);
|
||||
|
||||
// Extensions
|
||||
buf.put_u16(self.extensions.len() as u16);
|
||||
buf.put_slice(&self.extensions);
|
||||
|
||||
// 4. Патчим длину (переиспользуем твой метод)
|
||||
let total_len = (buf.len() - length_pos - 3) as u32;
|
||||
let len_bytes = total_len.to_be_bytes();
|
||||
buf[length_pos..length_pos + 3].copy_from_slice(&len_bytes[1..4]);
|
||||
|
||||
buf.freeze()
|
||||
}
|
||||
}
|
||||
|
||||
/// The TLS Record Layer structure.
|
||||
/// This is the outer envelope that wraps all TLS messages sent over the wire.
|
||||
#[derive(Debug)]
|
||||
pub struct TlsRecord {
|
||||
/// The type of data contained (Handshake, ApplicationData, etc.)
|
||||
pub content_type: ContentType,
|
||||
/// The record layer version (usually 0x0301 for legacy support)
|
||||
pub version: ProtocolVersion,
|
||||
|
||||
pub len: u16,
|
||||
/// The actual data being transported (e.g., a serialized ClientHello)
|
||||
pub payload: Bytes,
|
||||
}
|
||||
|
||||
impl TlsRecord {
|
||||
pub fn new(content_type: ContentType, version: ProtocolVersion, payload: Bytes) -> Self {
|
||||
Self {
|
||||
content_type,
|
||||
version,
|
||||
len: payload.len() as u16,
|
||||
payload,
|
||||
}
|
||||
}
|
||||
|
||||
/// Serializes the Record Layer header and payload.
|
||||
/// Wire Format: [Type (1)] [Version (2)] [Length (2)] [Payload (N)]
|
||||
pub fn serialize(&self) -> Bytes {
|
||||
let mut buf = BytesMut::with_capacity(5 + self.payload.len());
|
||||
|
||||
buf.put_u8(self.content_type as u8);
|
||||
buf.put_u16(self.version as u16);
|
||||
buf.put_u16(self.payload.len() as u16);
|
||||
buf.put_slice(&self.payload);
|
||||
|
||||
buf.freeze()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
use bytes::{BufMut, Bytes, BytesMut};
|
||||
|
||||
use crate::tlseng::types::{ContentType, ProtocolVersion};
|
||||
|
||||
/// The TLS Record Layer structure.
|
||||
/// This is the outer envelope that wraps all TLS messages sent over the wire.
|
||||
#[derive(Debug)]
|
||||
pub struct TlsRecord {
|
||||
/// The type of data contained (Handshake, ApplicationData, etc.)
|
||||
pub content_type: ContentType,
|
||||
/// The record layer version (usually 0x0301 for legacy support)
|
||||
pub version: ProtocolVersion,
|
||||
|
||||
pub len: u16,
|
||||
/// The actual data being transported (e.g., a serialized ClientHello)
|
||||
pub payload: Bytes,
|
||||
}
|
||||
|
||||
impl TlsRecord {
|
||||
pub fn new(content_type: ContentType, version: ProtocolVersion, payload: Bytes) -> Self {
|
||||
Self {
|
||||
content_type,
|
||||
version,
|
||||
len: payload.len() as u16,
|
||||
payload,
|
||||
}
|
||||
}
|
||||
|
||||
/// Serializes the Record Layer header and payload.
|
||||
/// Wire Format: [Type (1)] [Version (2)] [Length (2)] [Payload (N)]
|
||||
pub fn serialize(&self) -> Bytes {
|
||||
let mut buf = BytesMut::with_capacity(5 + self.payload.len());
|
||||
|
||||
buf.put_u8(self.content_type as u8);
|
||||
buf.put_u16(self.version as u16);
|
||||
buf.put_u16(self.payload.len() as u16);
|
||||
buf.put_slice(&self.payload);
|
||||
|
||||
buf.freeze()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/// TLS Content Types as defined in the TLS Record Protocol.
|
||||
/// These identify what is contained within the TLS Record payload.
|
||||
#[repr(u8)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum ContentType {
|
||||
/// Handshake messages (e.g., ClientHello, ServerHello)
|
||||
Handshake = 0x16,
|
||||
/// Encrypted application data (the actual traffic)
|
||||
ApplicationData = 0x17,
|
||||
/// Notification messages (e.g., CloseNotify or error signals)
|
||||
Alert = 0x15,
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for ContentType {
|
||||
type Error = &'static str;
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
0x16 => Ok(ContentType::Handshake),
|
||||
0x17 => Ok(ContentType::ApplicationData),
|
||||
0x15 => Ok(ContentType::Alert),
|
||||
_ => Err("This is not ContentType"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Known TLS protocol versions.
|
||||
/// Note: TLS 1.3 often uses legacy versions in headers for compatibility.
|
||||
#[repr(u16)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum ProtocolVersion {
|
||||
Tls10 = 0x0301,
|
||||
Tls12 = 0x0303,
|
||||
Tls13 = 0x0304,
|
||||
}
|
||||
|
||||
impl TryFrom<u16> for ProtocolVersion {
|
||||
type Error = &'static str;
|
||||
fn try_from(value: u16) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
0x0301 => Ok(ProtocolVersion::Tls10),
|
||||
0x0303 => Ok(ProtocolVersion::Tls12),
|
||||
0x0304 => Ok(ProtocolVersion::Tls13),
|
||||
_ => Err("This is not Protocol Version"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum HelloType {
|
||||
Client = 0x01,
|
||||
Server = 0x02,
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for HelloType {
|
||||
type Error = &'static str;
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
0x01 => Ok(HelloType::Client),
|
||||
0x02 => Ok(HelloType::Server),
|
||||
_ => Err("This is not Hello header"),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user