Refactor handshake

This commit is contained in:
Donald Knuth 2022-08-29 22:33:50 -04:00
parent 995d07282c
commit e79510cafe
7 changed files with 79 additions and 111 deletions

View file

@ -1,11 +1,11 @@
use crate::conf::BUFFER_SIZE; use crate::conf::BUFFER_SIZE;
use crate::crypto::Crypt;
use crate::file::{to_size_string, FileHandle, FileInfo}; use crate::file::{to_size_string, FileHandle, FileInfo};
use crate::handshake::Handshake; use crate::handshake::Handshake;
use crate::message::{ use crate::message::{
EncryptedMessage, FileNegotiationPayload, FileTransferPayload, Message, MessageStream, EncryptedMessage, FileNegotiationPayload, FileTransferPayload, Message, MessageStream,
}; };
use aes_gcm::Aes256Gcm;
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use futures::future::try_join_all; use futures::future::try_join_all;
@ -26,16 +26,18 @@ pub async fn send(file_paths: &Vec<PathBuf>, password: &String) -> Result<()> {
// Establish connection to server // Establish connection to server
let socket = TcpStream::connect("127.0.0.1:8080").await?; let socket = TcpStream::connect("127.0.0.1:8080").await?;
// Complete handshake, returning cipher used for encryption
let (handshake, s1) = Handshake::from_password(password); let (handshake, s1) = Handshake::from_password(password);
let (socket, cipher) = handshake.negotiate(socket, s1).await?; // Complete handshake, returning key used for encryption
let (socket, key) = handshake.negotiate(socket, s1).await?;
let mut stream = Message::to_stream(socket); let mut stream = Message::to_stream(socket);
let crypt = Crypt::new(&key);
// Complete file negotiation // Complete file negotiation
let handles = negotiate_files_up(handles, &mut stream, &cipher).await?; let handles = negotiate_files_up(handles, &mut stream, &crypt).await?;
// Upload negotiated files // Upload negotiated files
upload_encrypted_files(&mut stream, handles, &cipher).await?; upload_encrypted_files(&mut stream, handles, &crypt).await?;
println!("Done uploading.");
// Exit // Exit
Ok(()) Ok(())
@ -44,11 +46,12 @@ pub async fn send(file_paths: &Vec<PathBuf>, password: &String) -> Result<()> {
pub async fn receive(password: &String) -> Result<()> { pub async fn receive(password: &String) -> Result<()> {
let socket = TcpStream::connect("127.0.0.1:8080").await?; let socket = TcpStream::connect("127.0.0.1:8080").await?;
let (handshake, s1) = Handshake::from_password(password); let (handshake, s1) = Handshake::from_password(password);
let (socket, cipher) = handshake.negotiate(socket, s1).await?; let (socket, key) = handshake.negotiate(socket, s1).await?;
let mut stream = Message::to_stream(socket); let mut stream = Message::to_stream(socket);
let files = negotiate_files_down(&mut stream, &cipher).await?; let crypt = Crypt::new(&key);
let files = negotiate_files_down(&mut stream, &crypt).await?;
download_files(files, &mut stream, &cipher).await?; download_files(files, &mut stream, &crypt).await?;
return Ok(()); return Ok(());
} }
@ -63,23 +66,22 @@ pub async fn get_file_handles(file_paths: &Vec<PathBuf>) -> Result<Vec<FileHandl
pub async fn negotiate_files_up( pub async fn negotiate_files_up(
file_handles: Vec<FileHandle>, file_handles: Vec<FileHandle>,
stream: &mut MessageStream, stream: &mut MessageStream,
cipher: &Aes256Gcm, crypt: &Crypt,
) -> Result<Vec<FileHandle>> { ) -> Result<Vec<FileHandle>> {
let files = file_handles.iter().map(|fh| fh.to_file_info()).collect(); let files = file_handles.iter().map(|fh| fh.to_file_info()).collect();
let msg = EncryptedMessage::FileNegotiationMessage(FileNegotiationPayload { files }); let msg = EncryptedMessage::FileNegotiationMessage(FileNegotiationPayload { files });
let server_msg = msg.to_encrypted_message(cipher)?; let server_msg = msg.to_encrypted_message(crypt)?;
println!("server_msg encrypted: {:?}", server_msg); println!("server_msg encrypted: {:?}", server_msg);
stream.send(server_msg).await?; stream.send(server_msg).await?;
let reply_payload = match stream.next().await { let reply_payload = match stream.next().await {
Some(Ok(msg)) => match msg { Some(Ok(msg)) => match msg {
Message::EncryptedMessage(response) => response, Message::EncryptedMessage(response) => response,
_ => return Err(anyhow!("Expecting encrypted message back")),
}, },
_ => { _ => {
return Err(anyhow!("No response to negotiation message")); return Err(anyhow!("No response to negotiation message"));
} }
}; };
let plaintext_reply = EncryptedMessage::from_encrypted_message(cipher, &reply_payload)?; let plaintext_reply = EncryptedMessage::from_encrypted_message(crypt, &reply_payload)?;
let requested_paths: Vec<PathBuf> = match plaintext_reply { let requested_paths: Vec<PathBuf> = match plaintext_reply {
EncryptedMessage::FileNegotiationMessage(fnm) => { EncryptedMessage::FileNegotiationMessage(fnm) => {
fnm.files.into_iter().map(|f| f.path).collect() fnm.files.into_iter().map(|f| f.path).collect()
@ -94,18 +96,17 @@ pub async fn negotiate_files_up(
pub async fn negotiate_files_down( pub async fn negotiate_files_down(
stream: &mut MessageStream, stream: &mut MessageStream,
cipher: &Aes256Gcm, crypt: &Crypt,
) -> Result<Vec<FileInfo>> { ) -> Result<Vec<FileInfo>> {
let file_offer = match stream.next().await { let file_offer = match stream.next().await {
Some(Ok(msg)) => match msg { Some(Ok(msg)) => match msg {
Message::EncryptedMessage(response) => response, Message::EncryptedMessage(response) => response,
_ => return Err(anyhow!("Expecting encrypted message back")),
}, },
_ => { _ => {
return Err(anyhow!("No response to negotiation message")); return Err(anyhow!("No response to negotiation message"));
} }
}; };
let plaintext_offer = EncryptedMessage::from_encrypted_message(cipher, &file_offer)?; let plaintext_offer = EncryptedMessage::from_encrypted_message(crypt, &file_offer)?;
let requested_infos: Vec<FileInfo> = match plaintext_offer { let requested_infos: Vec<FileInfo> = match plaintext_offer {
EncryptedMessage::FileNegotiationMessage(fnm) => fnm.files, EncryptedMessage::FileNegotiationMessage(fnm) => fnm.files,
_ => return Err(anyhow!("Expecting file negotiation message back")), _ => return Err(anyhow!("Expecting file negotiation message back")),
@ -125,7 +126,7 @@ pub async fn negotiate_files_down(
let msg = EncryptedMessage::FileNegotiationMessage(FileNegotiationPayload { let msg = EncryptedMessage::FileNegotiationMessage(FileNegotiationPayload {
files: files.clone(), files: files.clone(),
}); });
let server_msg = msg.to_encrypted_message(cipher)?; let server_msg = msg.to_encrypted_message(crypt)?;
stream.send(server_msg).await?; stream.send(server_msg).await?;
Ok(files) Ok(files)
} }
@ -133,7 +134,7 @@ pub async fn negotiate_files_down(
pub async fn upload_encrypted_files( pub async fn upload_encrypted_files(
stream: &mut MessageStream, stream: &mut MessageStream,
handles: Vec<FileHandle>, handles: Vec<FileHandle>,
cipher: &Aes256Gcm, cipher: &Crypt,
) -> Result<()> { ) -> Result<()> {
let (tx, mut rx) = mpsc::unbounded_channel::<EncryptedMessage>(); let (tx, mut rx) = mpsc::unbounded_channel::<EncryptedMessage>();
for mut handle in handles { for mut handle in handles {
@ -188,7 +189,7 @@ pub async fn enqueue_file_chunks(
pub async fn download_files( pub async fn download_files(
file_infos: Vec<FileInfo>, file_infos: Vec<FileInfo>,
stream: &mut MessageStream, stream: &mut MessageStream,
cipher: &Aes256Gcm, cipher: &Crypt,
) -> Result<()> { ) -> Result<()> {
// for each file_info // for each file_info
let mut info_handles: HashMap<PathBuf, mpsc::UnboundedSender<(u64, Bytes)>> = HashMap::new(); let mut info_handles: HashMap<PathBuf, mpsc::UnboundedSender<(u64, Bytes)>> = HashMap::new();
@ -206,18 +207,15 @@ pub async fn download_files(
// println!("encrypted message received! {:?}", ec); // println!("encrypted message received! {:?}", ec);
match ec { match ec {
EncryptedMessage::FileTransferMessage(payload) => { EncryptedMessage::FileTransferMessage(payload) => {
println!("matched file transfer message"); // println!("matched file transfer message");
if let Some(tx) = info_handles.get(&payload.file_info.path) { if let Some(tx) = info_handles.get(&payload.file_info.path) {
println!("matched on filetype, sending to tx"); // println!("matched on filetype, sending to tx");
tx.send((payload.chunk_num, payload.chunk))? tx.send((payload.chunk_num, payload.chunk))?
}; };
}, },
_ => {println!("wrong msg")} _ => {println!("wrong msg")}
} }
} }
Some(Ok(_)) => {
println!("wrong msg");
}
Some(Err(e)) => { Some(Err(e)) => {
println!("Error {:?}", e); println!("Error {:?}", e);
} }

View file

@ -1,3 +1,5 @@
pub const ID_SIZE: usize = 32; pub const ID_SIZE: usize = 32; // Blake256 of password
pub const HANDSHAKE_MSG_SIZE: usize = 33; pub const HANDSHAKE_MSG_SIZE: usize = 33; // generated by Spake2
pub const BUFFER_SIZE: usize = 1024 * 1024 * 10; pub const PER_CLIENT_BUFFER: usize = 1024 * 1024; // buffer size allocated by server for each client
pub const BUFFER_SIZE: usize = 1024 * 1024 * 1024; // chunk size for files sent over wire
pub const NONCE_SIZE_IN_BYTES: usize = 96 / 8; // used for every encrypted message

4
src/connection.rs Normal file
View file

@ -0,0 +1,4 @@
pub struct Connection {
ms: MessageStream;
crypt: Crypt
}

View file

@ -1,3 +1,4 @@
use crate::conf::NONCE_SIZE_IN_BYTES;
use crate::message::EncryptedPayload; use crate::message::EncryptedPayload;
use aes_gcm::aead::{Aead, NewAead}; use aes_gcm::aead::{Aead, NewAead};
use aes_gcm::{Aes256Gcm, Key, Nonce}; // Or `Aes128Gcm` use aes_gcm::{Aes256Gcm, Key, Nonce}; // Or `Aes128Gcm`
@ -6,59 +7,37 @@ use bytes::Bytes;
use rand::{thread_rng, Rng}; use rand::{thread_rng, Rng};
// pub async fn handshake( pub struct Crypt {
// socket: TcpStream, cipher: Aes256Gcm,
// password: Bytes,
// id: Bytes,
// ) -> Result<(TcpStream, Aes256Gcm)> {
// let mut socket = socket;
// let (s1, outbound_msg) =
// Spake2::<Ed25519Group>::start_symmetric(&Password::new(password), &Identity::new(&id));
// println!("client - sending handshake msg");
// let mut handshake_msg = BytesMut::with_capacity(32 + 33);
// handshake_msg.extend_from_slice(&id);
// handshake_msg.extend_from_slice(&outbound_msg);
// let handshake_msg = handshake_msg.freeze();
// println!("client - handshake msg, {:?}", handshake_msg);
// println!("id: {:?}. msg: {:?}", id.clone(), outbound_msg.clone());
// socket.write_all(&handshake_msg).await?;
// let mut buffer = [0; 33];
// let n = socket.read_exact(&mut buffer).await?;
// println!("The bytes: {:?}", &buffer[..n]);
// let first_message = BytesMut::from(&buffer[..n]).freeze();
// println!("client - handshake msg responded to: {:?}", first_message);
// let key = match s1.finish(&first_message[..]) {
// Ok(key_bytes) => key_bytes,
// Err(e) => return Err(anyhow!(e.to_string())),
// };
// println!("Handshake successful. Key is {:?}", key);
// return Ok((socket, new_cipher(&key)));
// }
pub fn new_cipher(key: &Vec<u8>) -> Aes256Gcm {
let key = Key::from_slice(&key[..]);
Aes256Gcm::new(key)
} }
pub const NONCE_SIZE_IN_BYTES: usize = 96 / 8; impl Crypt {
pub fn encrypt(cipher: &Aes256Gcm, body: &Vec<u8>) -> Result<EncryptedPayload> { pub fn new(key: &Vec<u8>) -> Crypt {
let mut arr = [0u8; NONCE_SIZE_IN_BYTES]; let key = Key::from_slice(&key[..]);
thread_rng().try_fill(&mut arr[..])?; Crypt {
let nonce = Nonce::from_slice(&arr); cipher: Aes256Gcm::new(key),
let plaintext = body.as_ref(); }
match cipher.encrypt(nonce, plaintext) { }
Ok(body) => Ok(EncryptedPayload {
nonce: arr.to_vec(), pub fn encrypt(&self, body: &Vec<u8>) -> Result<EncryptedPayload> {
body, let mut arr = [0u8; NONCE_SIZE_IN_BYTES];
}), thread_rng().try_fill(&mut arr[..])?;
Err(_) => Err(anyhow!("Encryption error")), let nonce = Nonce::from_slice(&arr);
} let plaintext = body.as_ref();
} match self.cipher.encrypt(nonce, plaintext) {
Ok(body) => Ok(EncryptedPayload {
pub fn decrypt(cipher: &Aes256Gcm, payload: &EncryptedPayload) -> Result<Bytes> { nonce: arr.to_vec(),
let nonce = Nonce::from_slice(payload.nonce.as_ref()); body,
match cipher.decrypt(nonce, payload.body.as_ref()) { }),
Ok(payload) => Ok(Bytes::from(payload)), Err(_) => Err(anyhow!("Encryption error")),
Err(_) => Err(anyhow!("Decryption error")), }
}
pub fn decrypt(&self, payload: &EncryptedPayload) -> Result<Bytes> {
let nonce = Nonce::from_slice(payload.nonce.as_ref());
match self.cipher.decrypt(nonce, payload.body.as_ref()) {
Ok(payload) => Ok(Bytes::from(payload)),
Err(_) => Err(anyhow!("Decryption error")),
}
} }
} }

View file

@ -1,7 +1,5 @@
use crate::conf::{HANDSHAKE_MSG_SIZE, ID_SIZE}; use crate::conf::{HANDSHAKE_MSG_SIZE, ID_SIZE};
use crate::crypto::new_cipher;
use aes_gcm::Aes256Gcm; // Or `Aes128Gcm`
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use blake2::{Blake2s256, Digest}; use blake2::{Blake2s256, Digest};
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
@ -29,17 +27,12 @@ impl Handshake {
pub async fn from_socket(socket: TcpStream) -> Result<(Handshake, TcpStream)> { pub async fn from_socket(socket: TcpStream) -> Result<(Handshake, TcpStream)> {
let mut socket = socket; let mut socket = socket;
let mut buffer = BytesMut::with_capacity(ID_SIZE + HANDSHAKE_MSG_SIZE); let mut buffer = [0; ID_SIZE + HANDSHAKE_MSG_SIZE];
match socket.read_exact(&mut buffer).await? { let n = socket.read_exact(&mut buffer).await?;
65 => Ok((Handshake::from_buffer(buffer), socket)), // magic number to catch correct capacity println!("The bytes: {:?}", &buffer[..n]);
_ => return Err(anyhow!("invalid handshake buffer pulled from socket")), let mut outbound_msg = BytesMut::from(&buffer[..n]).freeze();
} let id = outbound_msg.split_to(ID_SIZE);
} Ok((Handshake { id, outbound_msg }, socket))
pub fn from_buffer(buffer: BytesMut) -> Handshake {
let mut outbound_msg = BytesMut::from(&buffer[..ID_SIZE + HANDSHAKE_MSG_SIZE]).freeze();
let id = outbound_msg.split_to(32);
Handshake { id, outbound_msg }
} }
pub fn to_bytes(self) -> Bytes { pub fn to_bytes(self) -> Bytes {
@ -53,10 +46,11 @@ impl Handshake {
self, self,
socket: TcpStream, socket: TcpStream,
s1: spake2::Spake2<spake2::Ed25519Group>, s1: spake2::Spake2<spake2::Ed25519Group>,
) -> Result<(TcpStream, Aes256Gcm)> { ) -> Result<(TcpStream, Vec<u8>)> {
let mut socket = socket; let mut socket = socket;
// println!("client - sending handshake msg"); let bytes = &self.to_bytes();
socket.write_all(&self.to_bytes()).await?; println!("client - sending handshake msg= {:?}", &bytes);
socket.write_all(&bytes).await?;
let mut buffer = [0; HANDSHAKE_MSG_SIZE]; let mut buffer = [0; HANDSHAKE_MSG_SIZE];
let n = socket.read_exact(&mut buffer).await?; let n = socket.read_exact(&mut buffer).await?;
let response = BytesMut::from(&buffer[..n]).freeze(); let response = BytesMut::from(&buffer[..n]).freeze();
@ -66,7 +60,7 @@ impl Handshake {
Err(e) => return Err(anyhow!(e.to_string())), Err(e) => return Err(anyhow!(e.to_string())),
}; };
println!("Handshake successful. Key is {:?}", key); println!("Handshake successful. Key is {:?}", key);
return Ok((socket, new_cipher(&key))); return Ok((socket, key));
} }
fn pass_to_bytes(password: &String) -> Bytes { fn pass_to_bytes(password: &String) -> Bytes {

View file

@ -1,7 +1,6 @@
use crate::crypto::{decrypt, encrypt}; use crate::crypto::Crypt;
use crate::file::FileInfo; use crate::file::FileInfo;
use aes_gcm::Aes256Gcm; // Or `Aes128Gcm`
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use bytes::Bytes; use bytes::Bytes;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -13,15 +12,7 @@ use tokio_util::codec::{Framed, LengthDelimitedCodec};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Message { pub enum Message {
HandshakeMessage(HandshakePayload),
EncryptedMessage(EncryptedPayload), EncryptedMessage(EncryptedPayload),
ErrorMessage(RuckError),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct HandshakePayload {
pub id: Bytes,
pub msg: Bytes,
} }
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
@ -49,8 +40,8 @@ pub struct FileTransferPayload {
} }
impl EncryptedMessage { impl EncryptedMessage {
pub fn from_encrypted_message(cipher: &Aes256Gcm, payload: &EncryptedPayload) -> Result<Self> { pub fn from_encrypted_message(crypt: &Crypt, payload: &EncryptedPayload) -> Result<Self> {
let raw = decrypt(cipher, payload)?; let raw = crypt.decrypt(payload)?;
let res = match bincode::deserialize(raw.as_ref()) { let res = match bincode::deserialize(raw.as_ref()) {
Ok(result) => result, Ok(result) => result,
Err(e) => { Err(e) => {
@ -60,7 +51,7 @@ impl EncryptedMessage {
}; };
Ok(res) Ok(res)
} }
pub fn to_encrypted_message(&self, cipher: &Aes256Gcm) -> Result<Message> { pub fn to_encrypted_message(&self, crypt: &Crypt) -> Result<Message> {
let raw = match bincode::serialize(&self) { let raw = match bincode::serialize(&self) {
Ok(result) => result, Ok(result) => result,
Err(e) => { Err(e) => {
@ -68,7 +59,7 @@ impl EncryptedMessage {
return Err(anyhow!("serialize error")); return Err(anyhow!("serialize error"));
} }
}; };
let payload = encrypt(cipher, &raw)?; let payload = crypt.encrypt(&raw)?;
Ok(Message::EncryptedMessage(payload)) Ok(Message::EncryptedMessage(payload))
} }
} }

View file

@ -1,4 +1,4 @@
use crate::conf::BUFFER_SIZE; use crate::conf::PER_CLIENT_BUFFER;
use crate::handshake::Handshake; use crate::handshake::Handshake;
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
@ -113,7 +113,7 @@ pub async fn handle_connection(
}; };
println!("Client upgraded"); println!("Client upgraded");
// The handshake cache should be empty for {id} at this point. // The handshake cache should be empty for {id} at this point.
let mut client_buffer = BytesMut::with_capacity(BUFFER_SIZE); let mut client_buffer = BytesMut::with_capacity(PER_CLIENT_BUFFER);
loop { loop {
tokio::select! { tokio::select! {
Some(msg) = client.rx.recv() => { Some(msg) = client.rx.recv() => {