Middle Proxy is so real
This commit is contained in:
178
src/transport/middle_proxy/codec.rs
Normal file
178
src/transport/middle_proxy/codec.rs
Normal file
@@ -0,0 +1,178 @@
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
|
||||
use crate::crypto::{AesCbc, crc32};
|
||||
use crate::error::{ProxyError, Result};
|
||||
use crate::protocol::constants::*;
|
||||
|
||||
pub(crate) fn build_rpc_frame(seq_no: i32, payload: &[u8]) -> Vec<u8> {
|
||||
let total_len = (4 + 4 + payload.len() + 4) as u32;
|
||||
let mut frame = Vec::with_capacity(total_len as usize);
|
||||
frame.extend_from_slice(&total_len.to_le_bytes());
|
||||
frame.extend_from_slice(&seq_no.to_le_bytes());
|
||||
frame.extend_from_slice(payload);
|
||||
let c = crc32(&frame);
|
||||
frame.extend_from_slice(&c.to_le_bytes());
|
||||
frame
|
||||
}
|
||||
|
||||
pub(crate) async fn read_rpc_frame_plaintext(
|
||||
rd: &mut (impl AsyncReadExt + Unpin),
|
||||
) -> Result<(i32, Vec<u8>)> {
|
||||
let mut len_buf = [0u8; 4];
|
||||
rd.read_exact(&mut len_buf).await.map_err(ProxyError::Io)?;
|
||||
let total_len = u32::from_le_bytes(len_buf) as usize;
|
||||
|
||||
if !(12..=(1 << 24)).contains(&total_len) {
|
||||
return Err(ProxyError::InvalidHandshake(format!(
|
||||
"Bad RPC frame length: {total_len}"
|
||||
)));
|
||||
}
|
||||
|
||||
let mut rest = vec![0u8; total_len - 4];
|
||||
rd.read_exact(&mut rest).await.map_err(ProxyError::Io)?;
|
||||
|
||||
let mut full = Vec::with_capacity(total_len);
|
||||
full.extend_from_slice(&len_buf);
|
||||
full.extend_from_slice(&rest);
|
||||
|
||||
let crc_offset = total_len - 4;
|
||||
let expected_crc = u32::from_le_bytes(full[crc_offset..crc_offset + 4].try_into().unwrap());
|
||||
let actual_crc = crc32(&full[..crc_offset]);
|
||||
if expected_crc != actual_crc {
|
||||
return Err(ProxyError::InvalidHandshake(format!(
|
||||
"CRC mismatch: 0x{expected_crc:08x} vs 0x{actual_crc:08x}"
|
||||
)));
|
||||
}
|
||||
|
||||
let seq_no = i32::from_le_bytes(full[4..8].try_into().unwrap());
|
||||
let payload = full[8..crc_offset].to_vec();
|
||||
Ok((seq_no, payload))
|
||||
}
|
||||
|
||||
pub(crate) fn build_nonce_payload(key_selector: u32, crypto_ts: u32, nonce: &[u8; 16]) -> [u8; 32] {
|
||||
let mut p = [0u8; 32];
|
||||
p[0..4].copy_from_slice(&RPC_NONCE_U32.to_le_bytes());
|
||||
p[4..8].copy_from_slice(&key_selector.to_le_bytes());
|
||||
p[8..12].copy_from_slice(&RPC_CRYPTO_AES_U32.to_le_bytes());
|
||||
p[12..16].copy_from_slice(&crypto_ts.to_le_bytes());
|
||||
p[16..32].copy_from_slice(nonce);
|
||||
p
|
||||
}
|
||||
|
||||
pub(crate) fn parse_nonce_payload(d: &[u8]) -> Result<(u32, u32, [u8; 16])> {
|
||||
if d.len() < 32 {
|
||||
return Err(ProxyError::InvalidHandshake(format!(
|
||||
"Nonce payload too short: {} bytes",
|
||||
d.len()
|
||||
)));
|
||||
}
|
||||
|
||||
let t = u32::from_le_bytes(d[0..4].try_into().unwrap());
|
||||
if t != RPC_NONCE_U32 {
|
||||
return Err(ProxyError::InvalidHandshake(format!(
|
||||
"Expected RPC_NONCE 0x{RPC_NONCE_U32:08x}, got 0x{t:08x}"
|
||||
)));
|
||||
}
|
||||
|
||||
let schema = u32::from_le_bytes(d[8..12].try_into().unwrap());
|
||||
let ts = u32::from_le_bytes(d[12..16].try_into().unwrap());
|
||||
let mut nonce = [0u8; 16];
|
||||
nonce.copy_from_slice(&d[16..32]);
|
||||
Ok((schema, ts, nonce))
|
||||
}
|
||||
|
||||
pub(crate) fn build_handshake_payload(
|
||||
our_ip: [u8; 4],
|
||||
our_port: u16,
|
||||
peer_ip: [u8; 4],
|
||||
peer_port: u16,
|
||||
) -> [u8; 32] {
|
||||
let mut p = [0u8; 32];
|
||||
p[0..4].copy_from_slice(&RPC_HANDSHAKE_U32.to_le_bytes());
|
||||
|
||||
// Keep C memory layout compatibility for PID IPv4 bytes.
|
||||
p[8..12].copy_from_slice(&our_ip);
|
||||
p[12..14].copy_from_slice(&our_port.to_le_bytes());
|
||||
let pid = (std::process::id() & 0xffff) as u16;
|
||||
p[14..16].copy_from_slice(&pid.to_le_bytes());
|
||||
let utime = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap_or_default()
|
||||
.as_secs() as u32;
|
||||
p[16..20].copy_from_slice(&utime.to_le_bytes());
|
||||
|
||||
p[20..24].copy_from_slice(&peer_ip);
|
||||
p[24..26].copy_from_slice(&peer_port.to_le_bytes());
|
||||
p
|
||||
}
|
||||
|
||||
pub(crate) fn cbc_encrypt_padded(
|
||||
key: &[u8; 32],
|
||||
iv: &[u8; 16],
|
||||
plaintext: &[u8],
|
||||
) -> Result<(Vec<u8>, [u8; 16])> {
|
||||
let pad = (16 - (plaintext.len() % 16)) % 16;
|
||||
let mut buf = plaintext.to_vec();
|
||||
let pad_pattern: [u8; 4] = [0x04, 0x00, 0x00, 0x00];
|
||||
for i in 0..pad {
|
||||
buf.push(pad_pattern[i % 4]);
|
||||
}
|
||||
|
||||
let cipher = AesCbc::new(*key, *iv);
|
||||
cipher
|
||||
.encrypt_in_place(&mut buf)
|
||||
.map_err(|e| ProxyError::Crypto(format!("CBC encrypt: {e}")))?;
|
||||
|
||||
let mut new_iv = [0u8; 16];
|
||||
if buf.len() >= 16 {
|
||||
new_iv.copy_from_slice(&buf[buf.len() - 16..]);
|
||||
}
|
||||
Ok((buf, new_iv))
|
||||
}
|
||||
|
||||
pub(crate) fn cbc_decrypt_inplace(
|
||||
key: &[u8; 32],
|
||||
iv: &[u8; 16],
|
||||
data: &mut [u8],
|
||||
) -> Result<[u8; 16]> {
|
||||
let mut new_iv = [0u8; 16];
|
||||
if data.len() >= 16 {
|
||||
new_iv.copy_from_slice(&data[data.len() - 16..]);
|
||||
}
|
||||
|
||||
AesCbc::new(*key, *iv)
|
||||
.decrypt_in_place(data)
|
||||
.map_err(|e| ProxyError::Crypto(format!("CBC decrypt: {e}")))?;
|
||||
Ok(new_iv)
|
||||
}
|
||||
|
||||
pub(crate) struct RpcWriter {
|
||||
pub(crate) writer: tokio::io::WriteHalf<tokio::net::TcpStream>,
|
||||
pub(crate) key: [u8; 32],
|
||||
pub(crate) iv: [u8; 16],
|
||||
pub(crate) seq_no: i32,
|
||||
}
|
||||
|
||||
impl RpcWriter {
|
||||
pub(crate) async fn send(&mut self, payload: &[u8]) -> Result<()> {
|
||||
let frame = build_rpc_frame(self.seq_no, payload);
|
||||
self.seq_no += 1;
|
||||
|
||||
let pad = (16 - (frame.len() % 16)) % 16;
|
||||
let mut buf = frame;
|
||||
let pad_pattern: [u8; 4] = [0x04, 0x00, 0x00, 0x00];
|
||||
for i in 0..pad {
|
||||
buf.push(pad_pattern[i % 4]);
|
||||
}
|
||||
|
||||
let cipher = AesCbc::new(self.key, self.iv);
|
||||
cipher
|
||||
.encrypt_in_place(&mut buf)
|
||||
.map_err(|e| ProxyError::Crypto(format!("{e}")))?;
|
||||
|
||||
if buf.len() >= 16 {
|
||||
self.iv.copy_from_slice(&buf[buf.len() - 16..]);
|
||||
}
|
||||
self.writer.write_all(&buf).await.map_err(ProxyError::Io)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user