big changes

This commit is contained in:
2026-02-25 18:09:20 +07:00
parent bf7d50bcef
commit 2835108b7f
56 changed files with 1111 additions and 775 deletions
+22
View File
@@ -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()
}
}
+7 -1
View File
@@ -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 {
+117
View File
@@ -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,
}
+3
View File
@@ -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
}
}
+5 -2
View File
@@ -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,
-232
View File
@@ -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()
}
}
+41
View File
@@ -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()
}
}
+62
View File
@@ -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"),
}
}
}