split codec and dynamic tcp sockers
This commit is contained in:
+19
-64
@@ -1,4 +1,4 @@
|
||||
use crate::crypto::SessionKeys;
|
||||
use crate::crypto::{SessionAuth, SessionKeys};
|
||||
use crate::nrxp::errors::{ErrorAction, ErrorStage, TlsError};
|
||||
use crate::parser::Parser;
|
||||
use crate::tlseng::ExtensionStack;
|
||||
@@ -69,16 +69,9 @@ impl TlsInterceptor for HandshakeMessage {
|
||||
let ext =
|
||||
ExtensionStack::parse(&mut BytesMut::from(hello.extensions.as_ref()))?
|
||||
.ok_or_else(|| {
|
||||
TlsError::new(
|
||||
ErrorStage::Handshake("Ext Err"),
|
||||
ErrorAction::Drop,
|
||||
Bytes::new(),
|
||||
)
|
||||
TlsError::new(ErrorStage::Handshake("Ext Err"), ErrorAction::Drop, Bytes::new())
|
||||
})?;
|
||||
return Ok(Some(HandshakeMessage::Client {
|
||||
base: hello,
|
||||
extensions: ext,
|
||||
}));
|
||||
return Ok(Some(HandshakeMessage::Client { base: hello, extensions: ext }));
|
||||
}
|
||||
}
|
||||
HelloType::Server => {
|
||||
@@ -86,16 +79,9 @@ impl TlsInterceptor for HandshakeMessage {
|
||||
let ext =
|
||||
ExtensionStack::parse(&mut BytesMut::from(hello.extensions.as_ref()))?
|
||||
.ok_or_else(|| {
|
||||
TlsError::new(
|
||||
ErrorStage::Handshake("Ext Err"),
|
||||
ErrorAction::Drop,
|
||||
Bytes::new(),
|
||||
)
|
||||
TlsError::new(ErrorStage::Handshake("Ext Err"), ErrorAction::Drop, Bytes::new())
|
||||
})?;
|
||||
return Ok(Some(HandshakeMessage::Server {
|
||||
base: hello,
|
||||
extensions: ext,
|
||||
}));
|
||||
return Ok(Some(HandshakeMessage::Server { base: hello, extensions: ext }));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -144,64 +130,33 @@ impl TlsBridge {
|
||||
) -> Result<Bytes, TlsError> {
|
||||
if let HandshakeMessage::Client { base, extensions } = client_msg {
|
||||
if base.session_id.len() != 32 {
|
||||
netrunner_logger::warn!(
|
||||
"❌ Auth failed: Expected 32 bytes SessionID, got {}. Client IP: ...",
|
||||
base.session_id.len()
|
||||
);
|
||||
return Err(TlsError::new(
|
||||
ErrorStage::Handshake("Invalid SessionID len"),
|
||||
ErrorAction::Drop,
|
||||
Bytes::new(),
|
||||
));
|
||||
return Err(TlsError::new(ErrorStage::Handshake("Invalid SessionID len"), ErrorAction::Drop, Bytes::new()));
|
||||
}
|
||||
|
||||
let mut received_tag = [0u8; 16];
|
||||
if base.session_id.len() >= 32 {
|
||||
received_tag.copy_from_slice(&base.session_id[16..32]);
|
||||
} else {
|
||||
return Err(TlsError::new(
|
||||
ErrorStage::Handshake("Short SessionID"),
|
||||
ErrorAction::Drop,
|
||||
Bytes::new(),
|
||||
));
|
||||
}
|
||||
received_tag.copy_from_slice(&base.session_id[16..32]);
|
||||
|
||||
if !keys.verify_auth_tag(&received_tag) {
|
||||
// ВАЖНО: Используем SessionAuth для проверки начального тега хендшейка
|
||||
let auth = SessionAuth::new(keys.get_auth_key());
|
||||
if !auth.verify_tag(&received_tag) {
|
||||
netrunner_logger::warn!("Unauthorized ClientHello: Auth Tag mismatch");
|
||||
return Err(TlsError::new(
|
||||
ErrorStage::Handshake("Auth Failed"),
|
||||
ErrorAction::Drop,
|
||||
Bytes::new(),
|
||||
));
|
||||
return Err(TlsError::new(ErrorStage::Handshake("Auth Failed"), ErrorAction::Drop, Bytes::new()));
|
||||
}
|
||||
|
||||
keys.update_keys(base.random, extensions, true)
|
||||
.map_err(|e| {
|
||||
netrunner_logger::error!(error = %e, "Server failed key update");
|
||||
TlsError::new(
|
||||
ErrorStage::Handshake("Key Exchange Failed"),
|
||||
ErrorAction::Drop,
|
||||
Bytes::new(),
|
||||
)
|
||||
})?;
|
||||
keys.update_keys(base.random, extensions, true).map_err(|e| {
|
||||
netrunner_logger::error!(error = %e, "Server failed key update");
|
||||
TlsError::new(ErrorStage::Handshake("Key Exchange Failed"), ErrorAction::Drop, Bytes::new())
|
||||
})?;
|
||||
|
||||
let server_pub_key = keys.public_key_bytes();
|
||||
|
||||
Ok(ServerHello::make_server_hello(
|
||||
base,
|
||||
&server_pub_key,
|
||||
keys.local_salt(),
|
||||
profile,
|
||||
))
|
||||
Ok(ServerHello::make_server_hello(base, &server_pub_key, keys.local_salt(), profile))
|
||||
} else {
|
||||
Err(TlsError::new(
|
||||
ErrorStage::Handshake("Expected ClientHello for SH generation"),
|
||||
ErrorAction::Drop,
|
||||
Bytes::new(),
|
||||
))
|
||||
Err(TlsError::new(ErrorStage::Handshake("Expected ClientHello"), ErrorAction::Drop, Bytes::new()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pack_app_data(buffer: Bytes) -> Bytes {
|
||||
TlsRecord::build_application_data(buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
+54
-131
@@ -1,130 +1,51 @@
|
||||
use bytes::{Bytes, BytesMut};
|
||||
|
||||
use crate::crypto::AeadPacker;
|
||||
use crate::crypto::ChaChaCipher;
|
||||
use crate::crypto::SessionKeys;
|
||||
use crate::crypto::{AeadPacker, ChaChaCipher, ChaChaStream, SessionAuth};
|
||||
use crate::nrxp::bridge::TlsBridge;
|
||||
use crate::nrxp::errors::{ErrorAction, ErrorStage, TlsError};
|
||||
use crate::nrxp::frame::{Frame, FrameType};
|
||||
use crate::parser::Parser;
|
||||
use crate::tlseng::{BrowserProfile, ServerProfile};
|
||||
|
||||
pub struct Codec {
|
||||
crypto: ChaChaCipher,
|
||||
session_keys: SessionKeys,
|
||||
staging: BytesMut,
|
||||
pub struct TxCodec {
|
||||
crypto: ChaChaStream,
|
||||
auth: SessionAuth,
|
||||
}
|
||||
|
||||
impl Codec {
|
||||
pub(crate) fn new(is_initiator: bool) -> Self {
|
||||
Self {
|
||||
crypto: ChaChaCipher::new(),
|
||||
session_keys: SessionKeys::new(is_initiator),
|
||||
staging: BytesMut::new(),
|
||||
}
|
||||
impl TxCodec {
|
||||
pub fn new(crypto: ChaChaStream, auth: SessionAuth) -> Self {
|
||||
Self { crypto, auth }
|
||||
}
|
||||
|
||||
pub(crate) fn make_client_handshake(
|
||||
&mut self,
|
||||
profile: &BrowserProfile,
|
||||
host: &str,
|
||||
) -> Result<Bytes, TlsError> {
|
||||
Ok(TlsBridge::wrap_client_hello(
|
||||
profile,
|
||||
host,
|
||||
&self.session_keys,
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn make_server_handshake(
|
||||
&mut self,
|
||||
buffer: &mut BytesMut,
|
||||
) -> Result<Bytes, TlsError> {
|
||||
let client_msg = TlsBridge::unpack_handshake(buffer)?.ok_or_else(|| {
|
||||
TlsError::new(
|
||||
ErrorStage::Handshake("No CH"),
|
||||
ErrorAction::Wait,
|
||||
Bytes::new(),
|
||||
)
|
||||
})?;
|
||||
|
||||
let server_hello_record = TlsBridge::wrap_server_hello(
|
||||
&client_msg,
|
||||
&mut self.session_keys,
|
||||
&ServerProfile::MODERN,
|
||||
)?;
|
||||
|
||||
let (w_key, w_iv, r_key, r_iv) = self.session_keys.get_aead_parameters();
|
||||
self.crypto.set_keys(w_key, w_iv, r_key, r_iv);
|
||||
|
||||
Ok(server_hello_record)
|
||||
}
|
||||
|
||||
pub(crate) fn process_handshake(&mut self, buffer: &mut BytesMut) -> Result<(), TlsError> {
|
||||
let mes_opt = TlsBridge::unpack_handshake(buffer)?;
|
||||
let mes = mes_opt.ok_or_else(|| {
|
||||
TlsError::new(
|
||||
ErrorStage::Handshake("Incomplete record"),
|
||||
ErrorAction::Wait,
|
||||
Bytes::new(),
|
||||
)
|
||||
})?;
|
||||
|
||||
let (w_key, w_iv, r_key, r_iv) = self
|
||||
.session_keys
|
||||
.update_keys(mes.random(), mes.extensions(), false)
|
||||
.map_err(|e| {
|
||||
netrunner_logger::error!(error = %e, "Client failed to update keys from ServerHello");
|
||||
TlsError::new(
|
||||
ErrorStage::Handshake("Keys update error"),
|
||||
ErrorAction::Drop,
|
||||
Bytes::new(),
|
||||
)
|
||||
})?;
|
||||
|
||||
self.crypto.set_keys(w_key, w_iv, r_key, r_iv);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn outbound(
|
||||
pub fn encode_frame(
|
||||
&mut self,
|
||||
stream_id: u32,
|
||||
frame_type: FrameType,
|
||||
payload: Bytes,
|
||||
) -> Result<Bytes, TlsError> {
|
||||
let tag = self.session_keys.generate_auth_tag();
|
||||
netrunner_logger::debug!(
|
||||
step = %(std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs() / 60),
|
||||
auth_key_hash = %hex::encode(&self.session_keys.auth_key_fingerprint()),
|
||||
generated_tag = %hex::encode(&tag[..4]),
|
||||
"OUTBOUND: Generated auth tag"
|
||||
);
|
||||
|
||||
let tag = self.auth.generate_current_tag();
|
||||
let frame = Frame::new(stream_id, frame_type, payload);
|
||||
let mut frame_bytes = frame.into_bytes(&tag);
|
||||
|
||||
let encrypted_payload = self.crypto.encrypt(&mut frame_bytes).map_err(|e| {
|
||||
netrunner_logger::error!("Encryption failed: {:?}", e);
|
||||
TlsError::new(
|
||||
ErrorStage::Tls("Encryption failed"),
|
||||
ErrorAction::Drop,
|
||||
Bytes::new(),
|
||||
)
|
||||
TlsError::new(ErrorStage::Tls("Encryption failed"), ErrorAction::Drop, Bytes::new())
|
||||
})?;
|
||||
|
||||
Ok(TlsBridge::pack_app_data(encrypted_payload))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encrypt_data(
|
||||
&mut self,
|
||||
stream_id: u32,
|
||||
frame_type: FrameType,
|
||||
data: Bytes,
|
||||
) -> Result<Bytes, TlsError> {
|
||||
self.outbound(stream_id, frame_type, data)
|
||||
pub struct RxCodec {
|
||||
crypto: ChaChaStream,
|
||||
auth: SessionAuth,
|
||||
staging: BytesMut,
|
||||
}
|
||||
|
||||
impl RxCodec {
|
||||
pub fn new(crypto: ChaChaStream, auth: SessionAuth, staging: BytesMut) -> Self {
|
||||
Self { crypto, auth, staging }
|
||||
}
|
||||
|
||||
pub fn inbound(&mut self, buffer: &mut BytesMut) -> Result<Option<Frame>, TlsError> {
|
||||
pub fn decode_inbound(&mut self, buffer: &mut BytesMut) -> Result<Option<Frame>, TlsError> {
|
||||
if !self.staging.is_empty() {
|
||||
if let Some(frame) = self.try_parse_frame()? {
|
||||
return Ok(Some(frame));
|
||||
@@ -136,43 +57,24 @@ impl Codec {
|
||||
e
|
||||
})? {
|
||||
let mut data_to_decrypt = BytesMut::from(app_data.payload);
|
||||
|
||||
let decrypted = self.crypto.decrypt(&mut data_to_decrypt).map_err(|_| {
|
||||
|
||||
let decrypted = self.crypto.decrypt(&mut data_to_decrypt).map_err(|e| {
|
||||
self.staging.clear();
|
||||
|
||||
let bad_data =
|
||||
Bytes::copy_from_slice(&data_to_decrypt[..data_to_decrypt.len().min(32)]);
|
||||
TlsError::new(
|
||||
ErrorStage::Tls("AEAD Decrypt Failed (MITM?)"),
|
||||
ErrorAction::Drop,
|
||||
bad_data,
|
||||
)
|
||||
let bad_data = Bytes::copy_from_slice(&data_to_decrypt[..data_to_decrypt.len().min(32)]);
|
||||
TlsError::new(ErrorStage::Tls("AEAD Decrypt Failed"), ErrorAction::Drop, bad_data)
|
||||
})?;
|
||||
|
||||
if decrypted.len() < 16 {
|
||||
self.staging.clear();
|
||||
return Err(TlsError::new(
|
||||
ErrorStage::Tls("Packet too short for auth"),
|
||||
ErrorAction::Drop,
|
||||
Bytes::new(),
|
||||
));
|
||||
return Err(TlsError::new(ErrorStage::Tls("Packet too short"), ErrorAction::Drop, Bytes::new()));
|
||||
}
|
||||
|
||||
let mut received_tag = [0u8; 16];
|
||||
received_tag.copy_from_slice(&decrypted[..16]);
|
||||
|
||||
if !self.session_keys.verify_auth_tag(&received_tag) {
|
||||
netrunner_logger::error!(
|
||||
expected_hash = %hex::encode(&self.session_keys.auth_key_fingerprint()),
|
||||
received = %hex::encode(&received_tag[..4]),
|
||||
"AUTH MISMATCH: Potential replay or MITM attack. Dropping connection."
|
||||
);
|
||||
if !self.auth.verify_tag(&received_tag) {
|
||||
self.staging.clear();
|
||||
return Err(TlsError::new(
|
||||
ErrorStage::Tls("Auth tag mismatch"),
|
||||
ErrorAction::Drop,
|
||||
Bytes::new(),
|
||||
));
|
||||
return Err(TlsError::new(ErrorStage::Tls("Auth mismatch"), ErrorAction::Drop, Bytes::new()));
|
||||
}
|
||||
|
||||
self.staging.extend_from_slice(&decrypted);
|
||||
@@ -191,12 +93,33 @@ impl Codec {
|
||||
Ok(None) => Ok(None),
|
||||
Err(_) => {
|
||||
self.staging.clear();
|
||||
Err(TlsError::new(
|
||||
ErrorStage::Tls("Parse error"),
|
||||
ErrorAction::Drop,
|
||||
Bytes::new(),
|
||||
))
|
||||
Err(TlsError::new(ErrorStage::Tls("Parse error"), ErrorAction::Drop, Bytes::new()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Codec {
|
||||
tx: Option<TxCodec>,
|
||||
rx: Option<RxCodec>,
|
||||
}
|
||||
|
||||
impl Codec {
|
||||
// УБРАЛИ staging из аргументов
|
||||
pub fn new(cipher: ChaChaCipher, auth_key: [u8; 32]) -> Self {
|
||||
let (rx_stream, tx_stream) = cipher.split();
|
||||
let auth = SessionAuth::new(auth_key);
|
||||
|
||||
Self {
|
||||
tx: Some(TxCodec::new(tx_stream, auth)),
|
||||
rx: Some(RxCodec::new(rx_stream, auth, BytesMut::with_capacity(4096))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn split(mut self) -> (RxCodec, TxCodec) {
|
||||
(
|
||||
self.rx.take().expect("RxCodec missing"),
|
||||
self.tx.take().expect("TxCodec missing"),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ mod codec;
|
||||
mod errors;
|
||||
mod frame;
|
||||
|
||||
pub(crate) use codec::Codec;
|
||||
pub(crate) use codec::{RxCodec, TxCodec, Codec};
|
||||
pub(crate) use errors::{ErrorAction, ErrorStage, TlsError};
|
||||
pub(crate) use frame::{Frame, FrameType};
|
||||
pub(crate) use bridge::TlsBridge;
|
||||
|
||||
Reference in New Issue
Block a user