diff --git a/Cargo.lock b/Cargo.lock index 8798cbc..7d59848 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -188,7 +188,7 @@ checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" dependencies = [ "byteorder", "digest 0.9.0", - "rand_core", + "rand_core 0.5.1", "subtle", "zeroize", ] @@ -346,7 +346,18 @@ checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.10.2+wasi-snapshot-preview1", ] [[package]] @@ -614,6 +625,12 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -656,13 +673,53 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +dependencies = [ + "libc", + "rand_chacha", + "rand_core 0.6.3", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.3", +] + [[package]] name = "rand_core" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom", + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom 0.2.4", +] + +[[package]] +name = "rand_hc" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +dependencies = [ + "rand_core 0.6.3", ] [[package]] @@ -685,6 +742,7 @@ dependencies = [ "bytes", "clap", "futures", + "rand", "serde", "spake2", "tokio", @@ -759,7 +817,7 @@ checksum = "7139ade210556eca57dfc299ec2454846ab6be09232eb1139a36e285ae7fd48e" dependencies = [ "curve25519-dalek", "hkdf", - "rand_core", + "rand_core 0.5.1", "sha2", ] @@ -907,6 +965,12 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index 0358121..8d71e61 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ bytes = { version = "1", features = ["serde"] } bincode = "1.3.3" clap = { version = "3.0.14", features = ["derive"] } futures = { version = "0.3.0", features = ["thread-pool"]} +rand = "0.8.4" serde = { version = "1.0", features = ["derive"] } spake2 = "0.3.1" tokio = { version = "1.16.1", features = ["full"] } diff --git a/src/client.rs b/src/client.rs index 1eb07b9..203d12b 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,9 +1,11 @@ use crate::crypto::handshake; +use crate::file::FileHandle; use crate::message::{Message, MessageStream}; use anyhow::Result; use blake2::{Blake2s256, Digest}; use bytes::{BufMut, Bytes, BytesMut}; +use futures::future::join_all; use futures::prelude::*; use std::path::PathBuf; use tokio::net::TcpStream; @@ -16,6 +18,13 @@ fn pass_to_bytes(password: &String) -> Bytes { } pub async fn send(file_paths: &Vec, password: &String) -> Result<()> { + let tasks = file_paths + .into_iter() + .map(|path| FileHandle::new(path.to_path_buf()).map(|f| f.map(|s| s.to_file_info()))); + let metadatas = join_all(tasks).await; + println!("mds: {:?}", metadatas); + return Ok(()); + let socket = TcpStream::connect("127.0.0.1:8080").await?; let mut stream = Message::to_stream(socket); @@ -27,21 +36,7 @@ pub async fn send(file_paths: &Vec, password: &String) -> Result<()> { ) .await?; - return upload_encrypted_files(stream, file_paths, key).await; - - // Send the value - // for path in paths.iter() { - // let b = path.to_str().unwrap().as_bytes(); - // let mut buf = BytesMut::with_capacity(1024); - // buf.put(&b[..]); - // let body = buf.freeze(); - // let m = Message { - // key: "abc".to_string(), - // from_sender: true, - // body: body, - // }; - // stream.send(m).await.unwrap(); - // } + // return upload_encrypted_files(stream, file_paths, key).await; } pub async fn receive(password: &String) -> Result<()> { diff --git a/src/crypto.rs b/src/crypto.rs index e98f20c..c6b4ad4 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -1,8 +1,11 @@ -use crate::message::{HandshakePayload, Message, MessageStream}; +use crate::message::{EncryptedPayload, HandshakePayload, Message, MessageStream}; +use aes_gcm::aead::{Aead, NewAead}; +use aes_gcm::{Aes256Gcm, Key, Nonce}; // Or `Aes128Gcm` use anyhow::{anyhow, Result}; use bytes::Bytes; use futures::prelude::*; +use rand::{thread_rng, Rng}; use spake2::{Ed25519Group, Identity, Password, Spake2}; pub async fn handshake( @@ -38,3 +41,32 @@ pub async fn handshake( println!("Handshake successful. Key is {:?}", key); return Ok((stream, Bytes::from(key))); } + +pub fn new_cypher(key: Bytes) -> Aes256Gcm { + let key = Key::from_slice(&key[..]); + Aes256Gcm::new(key) +} + +const NONCE_SIZE_IN_BYTES: usize = 96 / 8; + +pub fn encrypt(cipher: &Aes256Gcm, body: Bytes) -> Result { + let mut arr = [0u8; NONCE_SIZE_IN_BYTES]; + thread_rng().try_fill(&mut arr[..])?; + let nonce = Nonce::from_slice(&arr); + let plaintext = body.as_ref(); + match cipher.encrypt(nonce, plaintext) { + Ok(ciphertext) => Ok(EncryptedPayload { + nonce: Bytes::from(&arr[..]), + body: Bytes::from(ciphertext.as_ref()), + }), + Err(_) => anyhow!("Encryption error"), + } +} + +pub fn decrypt(cipher: &Aes256Gcm, payload: EncryptedPayload) -> Result { + let nonce = Nonce::from_slice(payload.nonce.as_ref()); + match cipher.decrypt(nonce, payload.body.as_ref()) { + Ok(_) => Ok(Bytes::from("hello")), + Err(_) => anyhow!("Decryption error"), + } +} diff --git a/src/file.rs b/src/file.rs new file mode 100644 index 0000000..d6e36be --- /dev/null +++ b/src/file.rs @@ -0,0 +1,51 @@ +use anyhow::Result; +use serde::{Deserialize, Serialize}; +use std::ffi::OsString; +use std::fs::Metadata; +use std::path::PathBuf; +use tokio::fs::File; + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct FileInfo { + name: OsString, + size: u64, +} + +pub struct FileHandle { + file: File, + md: Metadata, + path: PathBuf, +} + +impl FileHandle { + pub async fn new(path: PathBuf) -> Result { + let file = File::open(&path).await?; + let md = file.metadata().await?; + let fh = FileHandle { file, md, path }; + return Ok(fh); + } + + pub fn to_file_info(&self) -> FileInfo { + FileInfo { + name: match self.path.file_name() { + Some(s) => s.to_os_string(), + None => OsString::from("Unknown"), + }, + size: self.md.len(), + } + } +} + +const SUFFIX: [&'static str; 9] = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; +// Stolen: https://gitlab.com/forkbomb9/human_bytes-rs/-/blob/master/src/lib.rs +pub fn to_size_string(size: f64) -> String { + let base = size.log10() / 1024_f64.log10(); + let mut result = format!("{:.1}", 1024_f64.powf(base - base.floor()),) + .trim_end_matches(".0") + .to_owned(); + // Add suffix + result.push(' '); + result.push_str(SUFFIX[base.floor() as usize]); + + result +} diff --git a/src/main.rs b/src/main.rs index 6b1ff87..c5a0968 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ mod cli; mod client; mod crypto; +mod file; mod message; mod server; @@ -16,7 +17,7 @@ async fn main() -> Result<(), Box> { match &args.command { Commands::Send { paths, password } => { println!("Sending {:?}", paths); - send(&paths, password).await?; + send(paths, password).await?; } Commands::Receive { password } => { println!("Receiving password {}", password); diff --git a/src/message.rs b/src/message.rs index 5e42ea7..31b3f62 100644 --- a/src/message.rs +++ b/src/message.rs @@ -19,6 +19,12 @@ pub struct HandshakePayload { pub msg: Bytes, } +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct EncryptedPayload { + pub nonce: Bytes, + pub body: Bytes, +} + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum RuckError { NotHandshake, diff --git a/src/server.rs b/src/server.rs index 0ee4f4c..e3e6917 100644 --- a/src/server.rs +++ b/src/server.rs @@ -100,8 +100,7 @@ impl<'a> Client<'a> { true => { let (tx, rx) = mpsc::unbounded_channel(); tx.send(msg)?; - let shared = &mut state.lock().await; - shared.handshakes.insert(self.id.clone(), rx); + state.lock().await.handshakes.insert(self.id.clone(), rx); } false => { let shared = &mut state.lock().await; @@ -109,6 +108,7 @@ impl<'a> Client<'a> { tx.send(msg)?; } if let Some(mut rx) = shared.handshakes.remove(&self.id) { + drop(shared); if let Some(msg) = rx.recv().await { self.messages.send(msg).await?; }