use core::fmt; use std::net::{Ipv4Addr, SocketAddrV4}; use serde::{Deserialize, Serialize}; /// Parse a proxy config that XORs all traffic with the given key. /// /// A Xor configuration is represented by the proxy type `ProxyType::XorV2`. There used to be a `XorV1`, but it /// is deprecated and should not be used. /// /// The following bytes of an IPv6 address are interpreted to derive a Xor configuration: /// bytes 2-4 - u16le - proxy type - must be 0x03 /// bytes 4-8 - [u8; 4] - 4 bytes representing the proxy IPv4 address /// bytes 8-10 - u16le - port on which the proxy is listening /// bytes 10-16 - [u8; 6] - xor key bytes. 0x00 marks a premature end of the key /// Given the above, `2001:300:b9d5:9a75:3a04:eafd:1100:ad9e` will have the second hexlet (0x0300) /// represent the proxy type, the next 2 hexlets (0xb9d5,0x9a75) represent the IPv4 address for the /// proxy endpoint, the next hexlet (`3a04`) represents the port for the proxy endpoint, and /// the final 3 hexlets `eafd:1100:ad9e` represent the xor key (0xEA, 0xFD, 0x11). pub fn parse_xor(data: [u8; 12]) -> Result { let (ip_bytes, tail) = data.split_first_chunk::<4>().unwrap(); let (port_bytes, key_bytes) = tail.split_first_chunk::<2>().unwrap(); let key_bytes = <[u8; 6]>::try_from(key_bytes).unwrap(); let ip = Ipv4Addr::from(*ip_bytes); let port = u16::from_le_bytes(*port_bytes); if port == 0 { return Err(super::Error::InvalidPort(port)); } let addr = SocketAddrV4::new(ip, port); let key = XorKey::try_from(key_bytes)?; Ok(super::ProxyConfig { addr, obfuscation: Some(super::ObfuscationConfig::XorV2(key)), }) } /// A bunch of bytes, representing a "key" Simply meaning a slice of bytes that the data /// will be XORed with. #[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] pub struct XorKey { data: [u8; 6], len: usize, } impl XorKey { /// Return the XOR key material. Will always have at least length 1. pub fn key_data(&self) -> &[u8] { &self.data[0..self.len] } } impl fmt::Debug for XorKey { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "0x")?; for byte in self.key_data() { write!(f, "{byte:0>2x}")?; } Ok(()) } } impl TryFrom<[u8; 6]> for XorKey { type Error = super::Error; fn try_from(mut key_bytes: [u8; 6]) -> Result { let key_len = key_bytes .iter() .position(|b| *b == 0x00) .unwrap_or(key_bytes.len()); if key_len == 0 { return Err(super::Error::EmptyXorKey); } // Reset bytes after terminating null to zeros. // Allows simpler implementations of Eq and Hash key_bytes[key_len..].fill(0); Ok(Self { data: key_bytes, len: key_len, }) } } #[derive(Debug)] pub struct XorObfuscator { key: XorKey, key_index: usize, } impl XorObfuscator { pub fn new(key: XorKey) -> Self { Self { key, key_index: 0 } } } impl super::Obfuscator for XorObfuscator { fn obfuscate(&mut self, buffer: &mut [u8]) { let key_data = self.key.key_data(); for byte in buffer { *byte ^= key_data[self.key_index % key_data.len()]; self.key_index = (self.key_index + 1) % key_data.len(); } } } #[cfg(test)] mod tests { use std::net::{Ipv6Addr, SocketAddrV4}; use crate::config::xor::{XorKey, XorObfuscator}; use crate::config::{Error, ObfuscationConfig, Obfuscator, ProxyConfig}; #[test] fn xor_parsing() { struct Test { input: Ipv6Addr, expected: Result, } let tests = vec![ Test { input: "2001:300:7f00:1:3905:0102:304:506" .parse::() .unwrap(), expected: Ok(ProxyConfig { addr: "127.0.0.1:1337".parse::().unwrap(), obfuscation: Some(ObfuscationConfig::XorV2( XorKey::try_from([0x01, 0x02, 0x03, 0x04, 0x05, 0x06]).unwrap(), )), }), }, Test { input: "2001:300:7f00:1:3905:0100:304:506" .parse::() .unwrap(), expected: Ok(ProxyConfig { addr: "127.0.0.1:1337".parse::().unwrap(), obfuscation: Some(ObfuscationConfig::XorV2( XorKey::try_from([0x01, 0, 0, 0, 0, 0]).unwrap(), )), }), }, Test { input: "2001:300:c0a8:101:bb01:ff04:204:0" .parse::() .unwrap(), expected: Ok(ProxyConfig { addr: "192.168.1.1:443".parse::().unwrap(), obfuscation: Some(ObfuscationConfig::XorV2( XorKey::try_from([0xff, 0x04, 0x02, 0x04, 0, 0]).unwrap(), )), }), }, ]; for t in tests { let parsed = ProxyConfig::try_from(t.input); assert_eq!(parsed, t.expected); } } #[test] fn obfuscation() { const INPUT: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; let mut payload = INPUT.to_vec(); let xor_key = XorKey::try_from([0xff, 0x04, 0x02, 0x04, 0x00, 0x00]).unwrap(); let mut xor_obfuscator = XorObfuscator::new(xor_key); let mut xor_deobfuscator = XorObfuscator::new(xor_key); xor_obfuscator.obfuscate(&mut payload); assert_eq!( payload, &[0xfe, 0x06, 0x01, 0x00, 0xfa, 0x02, 0x05, 0x0c, 0xf6, 0x0e] ); xor_deobfuscator.obfuscate(&mut payload); assert_eq!(INPUT, payload.as_slice()); } // Before XOR-v2 there was XOR-v1, which is now deprecated. This test verifies that the old Xor // config does not deserialize. #[test] fn old_xor_addr() { match ProxyConfig::try_from( "2001:200:7f00:1:3905:0102:304:506" .parse::() .unwrap(), ) { Err(Error::XorV1Unsupported) => (), anything_else => panic!("Unexpected proxy config parse result: {anything_else:?}"), } } #[test] fn xor_key_debug_fmt() { let key = XorKey::try_from([0x01, 0xff, 0x31, 0x00, 0x00, 0x00]).unwrap(); let key_str = format!("{key:?}"); assert_eq!(key_str, "0x01ff31"); } }