diff --git a/Cargo.lock b/Cargo.lock index b8d5c92..c176da3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -30,6 +30,11 @@ name = "bitflags" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "bs58" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "byteorder" version = "1.3.1" @@ -625,6 +630,7 @@ name = "vpncloud" version = "0.9.1" dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "bs58 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", "daemonize 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "docopt 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -694,6 +700,7 @@ dependencies = [ "checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" +"checksum bs58 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0de79cfb98e7aa9988188784d8664b4b5dad6eaaa0863b91d9a4ed871d4f7a42" "checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" "checksum cc 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)" = "4390a3b5f4f6bce9c1d0c00128379df433e53777fdd30e92f16a529332baec4e" "checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" diff --git a/Cargo.toml b/Cargo.toml index 138ea97..b00af14 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ igd = "0.6" # Do not update, 0.7 has problems with exit by ctrl-c siphasher = "0.3" daemonize = "0.3" ring = "0.14" +bs58 = "*" [build-dependencies] cc = "^1" diff --git a/src/beacon.rs b/src/beacon.rs new file mode 100644 index 0000000..474de54 --- /dev/null +++ b/src/beacon.rs @@ -0,0 +1,164 @@ +use bs58; +use ring::digest; + +use std::str::FromStr; + +use super::util::{now, Encoder}; +use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr, SocketAddrV6, Ipv6Addr}; + + +fn base58_encode(data: &[u8]) -> String { + bs58::encode(data).into_string() +} + +fn base58_decode(data: &str) -> Vec { + bs58::decode(data).into_vec().unwrap() +} + +fn sha512(data: &[u8]) -> Vec { + digest::digest(&digest::SHA512, data).as_ref().iter().map(|b| *b).collect() +} + + +pub struct BeaconSerializer { + magic: Vec, + shared_key: String +} + +impl BeaconSerializer { + pub fn new(magic: &[u8], shared_key: &str) -> Self { + BeaconSerializer { + magic: magic.to_owned(), + shared_key: shared_key.to_string() + } + } + + fn seed(&self, key: &str) -> Vec { + let mut data = Vec::new(); + data.extend_from_slice(key.as_bytes()); + data.extend_from_slice(&self.magic); + data.extend_from_slice(self.shared_key.as_bytes()); + sha512(&data) + } + + fn mask_with_seed(&self, data: &[u8], key: &str) -> Vec { + let mask = self.seed(key); + let mut output = Vec::with_capacity(data.len()); + for i in 0..data.len() { + output.push(data[i] ^ mask[i]); + } + output + } + + fn begin(&self) -> String { + base58_encode(&self.seed("begin"))[0..5].to_string() + } + + fn end(&self) -> String { + base58_encode(&self.seed("end"))[0..5].to_string() + } + + fn peerlist_encode(&self, peers: &[SocketAddr]) -> String { + let mut data = Vec::new(); + // Add timestamp + data.append(&mut self.mask_with_seed(&(now() as u32).to_be_bytes(), "time")); + // Split addresses into v4 and v6 + let mut v4addrs = Vec::new(); + let mut v6addrs = Vec::new(); + for p in peers { + match *p { + SocketAddr::V4(addr) => v4addrs.push(addr), + SocketAddr::V6(addr) => v6addrs.push(addr) + } + } + // Add count of v4 addresses + data.append(&mut self.mask_with_seed(&[v4addrs.len() as u8], "v4count")); + // Add v4 addresses + for addr in v4addrs { + let mut dat = [0u8; 6]; + dat[0..4].copy_from_slice(&addr.ip().octets()); + Encoder::write_u16(addr.port(), &mut dat[4..]); + data.append(&mut self.mask_with_seed(&dat, "peer")); + } + // Add v6 addresses + for addr in v6addrs { + let mut dat = [0u8; 18]; + let ip = addr.ip().segments(); + Encoder::write_u16(ip[0], &mut dat[0..]); + Encoder::write_u16(ip[1], &mut dat[2..]); + Encoder::write_u16(ip[2], &mut dat[4..]); + Encoder::write_u16(ip[3], &mut dat[6..]); + Encoder::write_u16(ip[4], &mut dat[8..]); + Encoder::write_u16(ip[5], &mut dat[10..]); + Encoder::write_u16(ip[6], &mut dat[12..]); + Encoder::write_u16(ip[7], &mut dat[14..]); + Encoder::write_u16(addr.port(), &mut dat[16..]); + data.append(&mut self.mask_with_seed(&dat, "peer")); + } + base58_encode(&data) + } + + fn peerlist_decode(&self, data: &str) -> Vec { + let data = base58_decode(data); + let mut peers = Vec::new(); + //TODO: decode time + let mut pos = 4; + let v4count = self.mask_with_seed(&[data[pos]], "v4count")[0]; + pos += 1; + for _ in 0..v4count { + assert!(data.len() >= pos + 6); + let dat = self.mask_with_seed(&data[pos..pos+6], "peer"); + pos += 6; + let port = Encoder::read_u16(&dat[4..]); + let addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(dat[0], dat[1], dat[2], dat[3]), port)); + peers.push(addr); + } + let v6count = (data.len() - pos)/18; + for _ in 0..v6count { + assert!(data.len() >= pos + 18); + let dat = self.mask_with_seed(&data[pos..pos+18], "peer"); + pos += 18; + let mut ip = [0u16; 8]; + for i in 0..8 { + ip[i] = Encoder::read_u16(&dat[i*2..i*2+2]); + } + let port = Encoder::read_u16(&dat[16..]); + let addr = SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::new(ip[0], ip[1], ip[2], + ip[3], ip[4], ip[5], ip[6], ip[7]), port, 0, 0)); + peers.push(addr); + } + peers + } + + fn decode(&self, data: &str) -> Vec { + //TODO: remove anything that is not base58 + let mut peers = Vec::new(); + let begin = self.begin(); + let end = self.end(); + let mut pos = 0; + while let Some(found) = data[pos..].find(&begin) { + pos += found; + let start_pos = pos + begin.len(); + if let Some(found) = data[pos..].find(&end) { + let end_pos = pos + found; + peers.append(&mut self.peerlist_decode(&data[start_pos..end_pos])); + pos = end_pos + end.len(); + } else { + pos += begin.len(); + } + } + peers + } +} + + +pub fn test() { + let beacon = BeaconSerializer::new(b"vpnc", "mysecretkey"); + let mut peers = Vec::new(); + peers.push(SocketAddr::from_str("1.2.3.4:5678").unwrap()); + peers.push(SocketAddr::from_str("1.2.3.4:5678").unwrap()); + peers.push(SocketAddr::from_str("[::1]:5678").unwrap()); + let string = format!("{}{}{}", beacon.begin(), beacon.peerlist_encode(&peers), beacon.end()); + println!("{}", string); + println!("{:?}", beacon.decode(&string)); +} diff --git a/src/main.rs b/src/main.rs index 6cc2159..60f2edb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,6 +21,7 @@ extern crate igd; extern crate siphasher; extern crate daemonize; extern crate ring; +extern crate bs58; #[cfg(feature = "bench")] extern crate test; #[macro_use] pub mod util; @@ -35,6 +36,7 @@ pub mod poll; pub mod config; pub mod port_forwarding; pub mod traffic; +pub mod beacon; #[cfg(test)] mod tests; #[cfg(feature = "bench")] mod benches; @@ -262,6 +264,8 @@ fn run (config: Config) { } fn main() { + beacon::test(); + return; let args: Args = Docopt::new(USAGE).and_then(|d| d.deserialize()).unwrap_or_else(|e| e.exit()); if args.flag_version { Crypto::init(); diff --git a/src/udpmessage.rs b/src/udpmessage.rs index 46eaa61..30f0df8 100644 --- a/src/udpmessage.rs +++ b/src/udpmessage.rs @@ -188,7 +188,7 @@ pub fn encode<'a>(msg: &'a mut Message, mut buf: &'a mut [u8], magic: HeaderMagi SocketAddr::V4(addr) => v4addrs.push(addr), SocketAddr::V6(addr) => v6addrs.push(addr) } - }; + } assert!(v4addrs.len() <= 255); assert!(v6addrs.len() <= 255); let mut pos = start;