aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbnewbold <bnewbold@robocracy.org>2016-05-30 01:49:18 -0400
committerbnewbold <bnewbold@robocracy.org>2016-05-30 01:49:24 -0400
commit3a51fef71337f9e6683a3fe972e69cee92e1c097 (patch)
tree347a800b1519be2b79157e13708f5bef1d2c917f
parent37fb8945fad0a034d1565bc4f79f9ab524587fc0 (diff)
downloaducp-3a51fef71337f9e6683a3fe972e69cee92e1c097.tar.gz
ucp-3a51fef71337f9e6683a3fe972e69cee92e1c097.zip
BROKEN: initial implementation of crypto
I think it's going to be necessary to implement buffered reading after all.
-rw-r--r--Cargo.lock26
-rw-r--r--Cargo.toml6
-rw-r--r--README.md13
-rw-r--r--src/client.rs11
-rw-r--r--src/crypto.rs78
-rw-r--r--src/main.rs4
-rw-r--r--src/server.rs13
7 files changed, 141 insertions, 10 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 65a7d65..9dbbb37 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4,6 +4,8 @@ version = "0.1.0"
dependencies = [
"daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
+ "sodiumoxide 0.0.10 (registry+https://github.com/rust-lang/crates.io-index)",
"utp 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -62,6 +64,15 @@ version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "libsodium-sys"
+version = "0.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "log"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -150,6 +161,11 @@ version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "pkg-config"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "rand"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -180,6 +196,16 @@ version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "sodiumoxide"
+version = "0.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libsodium-sys 0.0.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "thread-id"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 183e38d..ad343e7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,6 +4,8 @@ version = "0.1.0"
authors = ["bnewbold <bnewbold@robocracy.org>"]
[dependencies]
-getopts = "0.2"
-daemonize = "0.2"
+getopts = "^0.2"
+daemonize = "^0.2"
utp = "*"
+sodiumoxide = "*"
+rustc-serialize = "0.3"
diff --git a/README.md b/README.md
index dd4f753..fd59a5f 100644
--- a/README.md
+++ b/README.md
@@ -17,6 +17,19 @@ that. There is a bunch of overhead sending small files, so if you have a lot of
those and a high-latency link you should probably `tar` things up first. There
also isn't any compression, so you might want to `gzip` that tarball.
+### Dependencies
+
+You need the Sodium (wrapper for NaCl) library and headers installed. On Linux,
+this is, eg, libsodium-dev.
+
+Uses sodiumoxide instead of rust-crypto because there aren't online docs for
+rust-crypto, and AFAIK Sodium and NaCl have been reviewed and rust-crypto has
+not.
+
+Uses rustc-serialize instead of serde, because serde seems more complicated
+(both nightly/compiler API and a non-macro API?) and doesn't seem to support
+base64.
+
### Usage
The command must be installed on both the local and remote machines.
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) {