aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client.rs11
-rw-r--r--src/crypto.rs78
-rw-r--r--src/main.rs4
-rw-r--r--src/server.rs13
4 files changed, 98 insertions, 8 deletions
diff --git a/src/client.rs b/src/client.rs
index 8827a11..1458998 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -1,7 +1,4 @@
-extern crate getopts;
-extern crate utp;
-
use super::common;
use std::string::String;
@@ -11,6 +8,8 @@ use std::process::exit;
use std::process::Command;
use getopts::Options;
use utp::{UtpSocket, UtpStream};
+use crypto::{SecretStream, key2string, string2key};
+use sodiumoxide::crypto::secretbox;
pub fn run_client(host: &str, local_file: &str, remote_file: &str, remote_is_dir: bool, is_recv: bool) {
println!("\thost: {}", host);
@@ -53,12 +52,16 @@ pub fn run_client(host: &str, local_file: &str, remote_file: &str, remote_is_dir
let mut socket = UtpSocket::connect((remote_host, remote_port)).unwrap();;
let mut stream: UtpStream = socket.into();
+ let mut stream = SecretStream::new(stream);
+ stream.key = string2key(remote_secret).unwrap();
+
if is_recv {
common::sink_files(&mut stream, local_file, remote_is_dir);
} else {
common::source_files(&mut stream, local_file, remote_is_dir);
}
- stream.close().unwrap();
+ // XXX: does Drop do this well enough?
+ //stream.close().unwrap();
}
fn usage_client(opts: Options) {
diff --git a/src/crypto.rs b/src/crypto.rs
new file mode 100644
index 0000000..726a420
--- /dev/null
+++ b/src/crypto.rs
@@ -0,0 +1,78 @@
+
+use std::{u8, u32};
+use std::io;
+use std::io::{Read,Write, ErrorKind};
+use sodiumoxide::crypto::secretbox;
+use sodiumoxide::crypto::secretbox::{Key, Nonce};
+use rustc_serialize::base64::{ToBase64, FromBase64, STANDARD};
+use std::mem::transmute;
+
+// TODO: handle case of splitting up writes > 2^32 bytes into multiple small writes
+
+pub struct SecretStream<S: Read+Write> {
+ read_nonce: Nonce,
+ write_nonce: Nonce,
+ pub key: Key,
+ inner: S,
+}
+
+impl<S: Read+Write> SecretStream<S> {
+ pub fn new(stream: S) -> SecretStream<S> {
+ SecretStream {
+ inner: stream,
+ read_nonce: secretbox::gen_nonce(),
+ write_nonce: secretbox::gen_nonce(),
+ key: secretbox::gen_key(),
+ }
+ }
+}
+
+impl<S: Read+Write> Read for SecretStream<S> {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ let mut header_buf = [0; 4];
+ try!(self.inner.read_exact(&mut header_buf));
+ let len: u32 = unsafe { transmute(header_buf) };
+ let len = len.to_be();
+ if len as usize > buf.len() {
+ return Err(io::Error::new(ErrorKind::Other,
+ format!("Buffer not big enough ({} < {})", buf.len(), len)));
+ }
+ try!(self.inner.read_exact(buf));
+ let cleartext = match secretbox::open(buf, &self.read_nonce, &self.key) {
+ Ok(cleartext) => cleartext,
+ Err(_) => { return Err(io::Error::new(ErrorKind::InvalidData,
+ "Failed to decrypt message (could mean corruption or malicious attack"))},
+ };
+ self.read_nonce.increment_le_inplace();
+ let len = len as usize;
+ buf.clone_from_slice(&cleartext[..len]);
+ return Ok(len as usize);
+ }
+}
+
+impl<S: Read+Write> Write for SecretStream<S> {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ assert!(buf.len() < u32::MAX as usize);
+ let len = buf.len() as u32;
+ let header_buf: [u8; 4] = unsafe { transmute(len.to_be()) };
+ try!(self.inner.write_all(&header_buf));
+ let ciphertext = secretbox::seal(buf, &self.write_nonce, &self.key);
+ self.write_nonce.increment_le_inplace();
+ try!(self.inner.write_all(&ciphertext[..]));
+ return Ok(len as usize);
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ return self.inner.flush();
+ }
+}
+
+pub fn key2string(key: &Key) -> String {
+ return (&(key[..])).to_base64(STANDARD);
+}
+
+pub fn string2key(s: &str) -> Result<Key, String> {
+ println!("KEYBYTES: {}", secretbox::KEYBYTES);
+ return Ok(Key::from_slice(&s.as_bytes().from_base64().unwrap()).unwrap());
+}
+
diff --git a/src/main.rs b/src/main.rs
index b5a9123..a1002c2 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -4,10 +4,13 @@
extern crate getopts;
extern crate utp;
+extern crate sodiumoxide;
+extern crate rustc_serialize;
mod client;
mod server;
mod common;
+mod crypto;
use std::str;
use std::env;
@@ -23,6 +26,7 @@ fn usage(opts: Options) {
fn main() {
let args: Vec<String> = env::args().collect();
+ sodiumoxide::init();
// First check for "hidden" server and client modes
if args.len() > 1 && args[1] == "server" {
diff --git a/src/server.rs b/src/server.rs
index 3c79984..62d4b19 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -1,6 +1,4 @@
-extern crate getopts;
-extern crate utp;
extern crate daemonize;
use super::common;
@@ -11,6 +9,8 @@ use std::env::home_dir;
use std::process::exit;
use getopts::Options;
use utp::{UtpSocket, UtpStream, UtpListener};
+use crypto::{SecretStream, key2string, string2key};
+use sodiumoxide::crypto::secretbox;
fn run_server(path: &str, is_recv: bool, recursive: bool, daemonize: bool) {
@@ -26,8 +26,10 @@ fn run_server(path: &str, is_recv: bool, recursive: bool, daemonize: bool) {
let listen_port = listener.local_addr().unwrap().port();
let listen_addr = listener.local_addr().unwrap().ip();
+ let secret_key = secretbox::gen_key();
+
// Send back details so client can connect
- println!("UCP CONNECT {} {} {}", listen_addr, listen_port, "<SECRET>");
+ println!("UCP CONNECT {} {} {}", listen_addr, listen_port, key2string(&secret_key));
// TODO: maybe wait for an ACK of some sort here before daemonizing?
@@ -52,13 +54,16 @@ fn run_server(path: &str, is_recv: bool, recursive: bool, daemonize: bool) {
let (mut socket, _src) = listener.accept().unwrap();
println!("Got connection from {}", socket.peer_addr().unwrap());
let mut stream: UtpStream = socket.into();
+ let mut stream = SecretStream::new(stream);
+ stream.key = secret_key;
if is_recv {
common::sink_files(&mut stream, path, recursive);
} else {
common::source_files(&mut stream, path, recursive);
}
- stream.close().unwrap();
+ // XXX: does Drop do this well enough?
+ //stream.close().unwrap();
}
fn usage_server(opts: Options) {