Switching to async-psec

This commit is contained in:
Matéo Duparc 2021-05-17 14:37:04 +02:00
parent 8adcef8852
commit 275c2972df
Signed by: hardcoresushi
GPG Key ID: 007F84120107191E
11 changed files with 2835 additions and 593 deletions

5
.gitignore vendored
View File

@ -1,3 +1,2 @@
Cargo.lock
target
local
/target
local

2754
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -5,39 +5,36 @@ authors = ["Hardcore Sushi <hardcore.sushi@disroot.org>"]
edition = "2018"
[dependencies]
rand-8 = {package = "rand", version = "0.8.3"}
rand-7 = {package = "rand", version = "0.7.3"}
tokio = {version = "1", features = ["rt", "rt-multi-thread", "macros", "net", "io-util"]}
async-trait = "0.1.5"
rand = "0.8"
rand-7 = { package = "rand", version = "0.7.3" }
tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros", "net", "io-util"] }
async-psec = { version = "0.1", git = "https://forge.chapril.org/hardcoresushi/async-psec", features = ["split"] }
lazy_static = "1.4"
socket2 = "0.4.0"
rusqlite = {version = "0.25.1", features = ["bundled"]}
socket2 = "0.4"
rusqlite = { version = "0.25.1", features = ["bundled"] }
ed25519-dalek = "1" #for singing
x25519-dalek = "1.1" #for shared secret
sha2 = "0.9.3"
hkdf = "0.11.0"
aes-gcm = "0.9.0" #PSEC
aes-gcm-siv = "0.10.0" #Database
hmac = "0.11.0"
hex = "0.4.3"
strum_macros = "0.20.1" #display enums
sha2 = "0.9"
aes-gcm = "0.9"
aes-gcm-siv = "0.10" #Database
hkdf = "0.11"
hex = "0.4"
actix-web = "3"
actix-multipart = "0.3"
time = "0.2.25" #needed for actix cookies
time = "0.2" #needed for actix cookies
futures = "0.3"
tungstenite = "0.13.0" #websocket
serde = "1.0.124" #serialization
html-escape = "0.2.7"
tungstenite = "0.13" #websocket
serde = "1.0" #serialization
html-escape = "0.2"
sanitize-filename = "0.3"
platform-dirs = "0.3.0"
uuid = {version = "0.8", features = ["v4"]}
webbrowser = "0.5.5"
platform-dirs = "0.3"
uuid = { version = "0.8", features = ["v4"] }
webbrowser = "0.5"
libmdns = "0.6" #mDNS advertiser
multicast_dns = "0.5" #mDNS browser
if-addrs = "0.6"
base64 = "0.13.0"
scrypt = "0.7.0"
zeroize = "1.2.0"
base64 = "0.13"
scrypt = "0.7"
zeroize = "1.2"
[build-dependencies]
html-minifier = "3.0.11"
html-minifier = "3.0"

View File

@ -1,148 +1,18 @@
use std::convert::TryInto;
use std::{convert::TryInto, fmt::Display};
use hkdf::Hkdf;
use sha2::Sha384;
use hmac::{Hmac, Mac, NewMac};
use scrypt::{scrypt, Params};
use rand_8::{RngCore, rngs::OsRng};
use rand::{RngCore, rngs::OsRng};
use aes_gcm::{aead::Aead, NewAead, Nonce};
use aes_gcm_siv::Aes256GcmSiv;
use zeroize::Zeroize;
use strum_macros::Display;
use crate::utils::*;
pub const HASH_OUTPUT_LEN: usize = 48; //SHA384
const KEY_LEN: usize = 16;
pub const IV_LEN: usize = 12;
pub const AES_TAG_LEN: usize = 16;
pub const SALT_LEN: usize = 32;
const PASSWORD_HASH_LEN: usize = 32;
pub const MASTER_KEY_LEN: usize = 32;
pub fn iv_to_nonce(iv: &[u8], counter: &mut usize) -> Vec<u8> {
let mut counter_bytes = vec![0; 4];
counter_bytes.extend_from_slice(&counter.to_be_bytes());
let r: Vec<u8> = iv.iter().zip(counter_bytes.iter()).map(|(a, b)| a^b).collect();
*counter += 1;
r
}
fn hkdf_expand_label(key: &[u8], label: &str, context: Option<&[u8]>, okm: &mut [u8]) {
let hkdf = Hkdf::<Sha384>::from_prk(key).unwrap();
//can't set info conditionnally because of different array size
match context {
Some(context) => {
let info = [&(label.len() as u32).to_be_bytes(), label.as_bytes(), &(context.len() as u32).to_be_bytes(), context];
hkdf.expand_multi_info(&info, okm).unwrap();
}
None => {
let info = [&(label.len() as u32).to_be_bytes(), label.as_bytes()];
hkdf.expand_multi_info(&info, okm).unwrap();
}
};
}
pub struct HandshakeKeys {
pub local_key: [u8; KEY_LEN],
pub local_iv: [u8; IV_LEN],
pub local_handshake_traffic_secret: [u8; HASH_OUTPUT_LEN],
pub peer_key: [u8; KEY_LEN],
pub peer_iv: [u8; IV_LEN],
pub peer_handshake_traffic_secret: [u8; HASH_OUTPUT_LEN],
pub handshake_secret: [u8; HASH_OUTPUT_LEN],
}
impl HandshakeKeys {
pub fn derive_keys(shared_secret: [u8; 32], handshake_hash: [u8; HASH_OUTPUT_LEN], i_am_bob: bool) -> HandshakeKeys {
let (handshake_secret, _) = Hkdf::<Sha384>::extract(None, &shared_secret);
let local_label = "handshake".to_owned() + if i_am_bob {"i_am_bob"} else {"i_am_alice"};
let mut local_handshake_traffic_secret = [0; HASH_OUTPUT_LEN];
hkdf_expand_label(handshake_secret.as_slice(), &local_label, Some(&handshake_hash), &mut local_handshake_traffic_secret);
let peer_label = "handshake".to_owned() + if i_am_bob {"i_am_alice"} else {"i_am_bob"};
let mut peer_handshake_traffic_secret = [0; HASH_OUTPUT_LEN];
hkdf_expand_label(handshake_secret.as_slice(), &peer_label, Some(&handshake_hash), &mut peer_handshake_traffic_secret);
let mut local_handshake_key = [0; KEY_LEN];
hkdf_expand_label(&local_handshake_traffic_secret, "key", None, &mut local_handshake_key);
let mut local_handshake_iv = [0; IV_LEN];
hkdf_expand_label(&local_handshake_traffic_secret, "iv", None, &mut local_handshake_iv);
let mut peer_handshake_key = [0; KEY_LEN];
hkdf_expand_label(&peer_handshake_traffic_secret, "key", None, &mut peer_handshake_key);
let mut peer_handshake_iv = [0; IV_LEN];
hkdf_expand_label(&peer_handshake_traffic_secret,"iv", None, &mut peer_handshake_iv);
HandshakeKeys {
local_key: local_handshake_key,
local_iv: local_handshake_iv,
local_handshake_traffic_secret: local_handshake_traffic_secret,
peer_key: peer_handshake_key,
peer_iv: peer_handshake_iv,
peer_handshake_traffic_secret: peer_handshake_traffic_secret,
handshake_secret: to_array_48(handshake_secret.as_slice())
}
}
}
pub struct ApplicationKeys {
pub local_key: [u8; KEY_LEN],
pub local_iv: [u8; IV_LEN],
pub peer_key: [u8; KEY_LEN],
pub peer_iv: [u8; IV_LEN],
}
impl ApplicationKeys {
pub fn derive_keys(handshake_secret: [u8; HASH_OUTPUT_LEN], handshake_hash: [u8; HASH_OUTPUT_LEN], i_am_bob: bool) -> ApplicationKeys {
let mut derived_secret = [0; HASH_OUTPUT_LEN];
hkdf_expand_label(&handshake_secret, "derived", None, &mut derived_secret);
let (master_secret, _) = Hkdf::<Sha384>::extract(Some(&derived_secret), b"");
let local_label = "application".to_owned() + if i_am_bob {"i_am_bob"} else {"i_am_alice"};
let mut local_application_traffic_secret = [0; HASH_OUTPUT_LEN];
hkdf_expand_label(&master_secret, &local_label, Some(&handshake_hash), &mut local_application_traffic_secret);
let peer_label = "application".to_owned() + if i_am_bob {"i_am_alice"} else {"i_am_bob"};
let mut peer_application_traffic_secret = [0; HASH_OUTPUT_LEN];
hkdf_expand_label(&master_secret, &peer_label, Some(&handshake_hash), &mut peer_application_traffic_secret);
let mut local_application_key = [0; KEY_LEN];
hkdf_expand_label(&local_application_traffic_secret, "key", None, &mut local_application_key);
let mut local_application_iv = [0; IV_LEN];
hkdf_expand_label(&local_application_traffic_secret, "iv", None, &mut local_application_iv);
let mut peer_application_key = [0; KEY_LEN];
hkdf_expand_label(&peer_application_traffic_secret, "key", None, &mut peer_application_key);
let mut peer_application_iv = [0; IV_LEN];
hkdf_expand_label(&peer_application_traffic_secret,"iv", None, &mut peer_application_iv);
ApplicationKeys {
local_key: local_application_key,
local_iv: local_application_iv,
peer_key: peer_application_key,
peer_iv: peer_application_iv,
}
}
}
pub fn compute_handshake_finished(local_handshake_traffic_secret: [u8; HASH_OUTPUT_LEN], handshake_hash: [u8; HASH_OUTPUT_LEN]) -> [u8; HASH_OUTPUT_LEN] {
let mut finished_key = [0; HASH_OUTPUT_LEN];
hkdf_expand_label(&local_handshake_traffic_secret, "finished", None, &mut finished_key);
let mut hmac = Hmac::<Sha384>::new_from_slice(&finished_key).unwrap();
hmac.update(&handshake_hash);
hmac.finalize().into_bytes().as_slice().try_into().unwrap()
}
pub fn verify_handshake_finished(peer_handshake_finished: [u8; HASH_OUTPUT_LEN], peer_handshake_traffic_secret: [u8; HASH_OUTPUT_LEN], handshake_hash: [u8; HASH_OUTPUT_LEN]) -> bool {
let mut peer_finished_key = [0; HASH_OUTPUT_LEN];
hkdf_expand_label(&peer_handshake_traffic_secret, "finished", None, &mut peer_finished_key);
let mut hmac = Hmac::<Sha384>::new_from_slice(&peer_finished_key).unwrap();
hmac.update(&handshake_hash);
hmac.verify(&peer_handshake_finished).is_ok()
}
pub fn generate_fingerprint(public_key: &[u8]) -> String {
let mut raw_fingerprint = [0; 16];
Hkdf::<Sha384>::new(None, public_key).expand(&[], &mut raw_fingerprint).unwrap();
@ -150,7 +20,6 @@ pub fn generate_fingerprint(public_key: &[u8]) -> String {
}
pub fn generate_master_key() -> [u8; MASTER_KEY_LEN] {
let mut master_key = [0; MASTER_KEY_LEN];
OsRng.fill_bytes(&mut master_key);
@ -169,12 +38,21 @@ pub fn encrypt_data(data: &[u8], master_key: &[u8]) -> Result<Vec<u8>, CryptoErr
Ok(cipher_text)
}
#[derive(Display, Debug, PartialEq, Eq)]
#[derive(Debug, PartialEq, Eq)]
pub enum CryptoError {
DecryptionFailed,
InvalidLength
}
impl Display for CryptoError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
CryptoError::DecryptionFailed => "Decryption failed",
CryptoError::InvalidLength => "Invalid length",
})
}
}
pub fn decrypt_data(data: &[u8], master_key: &[u8]) -> Result<Vec<u8>, CryptoError> {
if data.len() <= IV_LEN || master_key.len() != MASTER_KEY_LEN {
return Err(CryptoError::InvalidLength);

View File

@ -1,6 +1,6 @@
use std::convert::TryInto;
use crypto::CryptoError;
use ed25519_dalek::{Keypair, Signer, SIGNATURE_LENGTH, PUBLIC_KEY_LENGTH};
use ed25519_dalek::{Keypair, PUBLIC_KEY_LENGTH};
use rusqlite::{Connection, params};
use platform_dirs::AppDirs;
use utils::to_uuid_bytes;
@ -59,16 +59,12 @@ pub struct Contact {
pub struct Identity {
pub name: String,
keypair: Keypair,
pub keypair: Keypair,
pub master_key: [u8; crypto::MASTER_KEY_LEN],
pub use_padding: bool,
}
impl Identity {
pub fn sign(&self, input: &[u8]) -> [u8; SIGNATURE_LENGTH] {
self.keypair.sign(input).to_bytes()
}
pub fn get_public_key(&self) -> [u8; PUBLIC_KEY_LENGTH] {
self.keypair.public.to_bytes()

View File

@ -2,6 +2,7 @@ mod key_value_table;
mod identity;
mod crypto;
mod session_manager;
mod protocol;
mod utils;
mod ui_interface;
mod constants;
@ -13,14 +14,14 @@ use actix_web::{App, HttpMessage, HttpRequest, HttpResponse, HttpServer, http::{
use actix_multipart::Multipart;
use tungstenite::Message;
use futures::{StreamExt, TryStreamExt};
use rand_8::{RngCore, rngs::OsRng};
use rand::{RngCore, rngs::OsRng};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use platform_dirs::AppDirs;
use zeroize::Zeroize;
use utils::escape_double_quote;
use identity::Identity;
use session_manager::{SessionManager, SessionCommand, protocol};
use session_manager::{SessionManager, SessionCommand};
use ui_interface::UiConnection;
async fn start_websocket_server(global_vars: Arc<RwLock<GlobalVars>>) -> u16 {
@ -159,11 +160,10 @@ async fn websocket_worker(mut ui_connection: UiConnection, global_vars: Arc<RwLo
"send" => {
let session_id: usize = args[1].parse().unwrap();
let buffer = protocol::new_message(msg[args[0].len()+args[1].len()+2..].to_string());
match session_manager.send_command(&session_id, SessionCommand::Send {
if session_manager.send_command(&session_id, SessionCommand::Send {
buff: buffer.clone()
}).await {
Ok(_) => session_manager.store_msg(&session_id, true, buffer),
Err(e) => print_error!(e)
session_manager.store_msg(&session_id, true, buffer);
}
}
"large_files" => {
@ -172,33 +172,25 @@ async fn websocket_worker(mut ui_connection: UiConnection, global_vars: Arc<RwLo
for n in (2..args.len()).step_by(2) {
file_info.push((args[n].parse::<u64>().unwrap(), base64::decode(args[n+1]).unwrap()));
}
if let Err(e) = session_manager.send_command(&session_id, SessionCommand::Send {
session_manager.send_command(&session_id, SessionCommand::Send {
buff: protocol::ask_large_files(file_info)
}).await {
print_error!(e);
}
}).await;
}
"download" => {
let session_id: usize = args[1].parse().unwrap();
if let Err(e) = session_manager.send_command(&session_id, SessionCommand::Send {
session_manager.send_command(&session_id, SessionCommand::Send {
buff: vec![protocol::Headers::ACCEPT_LARGE_FILES]
}).await {
print_error!(e);
}
}).await;
}
"abort" => {
let session_id: usize = args[1].parse().unwrap();
if let Err(e) = session_manager.send_command(&session_id, SessionCommand::Send {
session_manager.send_command(&session_id, SessionCommand::Send {
buff: vec![protocol::Headers::ABORT_FILES_TRANSFER]
}).await {
print_error!(e);
}
}).await;
}
"sending_ended" => {
let session_id: usize = args[1].parse().unwrap();
if let Err(e) = session_manager.send_command(&session_id, SessionCommand::SendingEnded).await {
print_error!(e);
}
session_manager.send_command(&session_id, SessionCommand::SendingEnded).await;
}
"load_msgs" => {
let session_id: usize = args[1].parse().unwrap();
@ -234,11 +226,9 @@ async fn websocket_worker(mut ui_connection: UiConnection, global_vars: Arc<RwLo
}
"ask_name" => {
let session_id: usize = args[1].parse().unwrap();
if let Err(e) = session_manager.send_command(&session_id, SessionCommand::Send {
session_manager.send_command(&session_id, SessionCommand::Send {
buff: protocol::ask_name()
}).await {
print_error!(e);
}
}).await;
}
"set_use_padding" => {
let use_padding: bool = args[1].parse().unwrap();
@ -357,20 +347,17 @@ async fn handle_send_file(req: HttpRequest, mut payload: Multipart) -> HttpRespo
while let Some(Ok(chunk)) = field.next().await {
buffer.extend(chunk);
}
match global_vars_read.session_manager.send_command(&session_id, SessionCommand::Send {
if global_vars_read.session_manager.send_command(&session_id, SessionCommand::Send {
buff: protocol::file(filename, &buffer)
}).await {
Ok(_) => {
match global_vars_read.session_manager.store_file(&session_id, &buffer) {
Ok(file_uuid) => {
let msg = [&[protocol::Headers::FILE][..], file_uuid.as_bytes(), filename.as_bytes()].concat();
global_vars_read.session_manager.store_msg(&session_id, true, msg);
return HttpResponse::Ok().body(file_uuid.to_string());
}
Err(e) => print_error!(e)
match global_vars_read.session_manager.store_file(&session_id, &buffer) {
Ok(file_uuid) => {
let msg = [&[protocol::Headers::FILE][..], file_uuid.as_bytes(), filename.as_bytes()].concat();
global_vars_read.session_manager.store_msg(&session_id, true, msg);
return HttpResponse::Ok().body(file_uuid.to_string());
}
Err(e) => print_error!(e)
}
Err(e) => print_error!(e)
}
} else {
let (ack_sender, mut ack_receiver) = mpsc::channel(1);
@ -394,22 +381,20 @@ async fn handle_send_file(req: HttpRequest, mut payload: Multipart) -> HttpRespo
break;
}
}
if let Err(e) = global_vars_read.session_manager.send_command(&session_id, SessionCommand::EncryptFileChunk{
if !global_vars_read.session_manager.send_command(&session_id, SessionCommand::EncryptFileChunk{
plain_text: chunk_buffer.clone()
}).await {
print_error!(e);
return HttpResponse::InternalServerError().finish();
}
if !match ack_receiver.recv().await {
Some(should_continue) => {
//send previous encrypted chunk even if transfert is aborted to keep PSEC nonces syncrhonized
if let Err(e) = global_vars_read.session_manager.send_command(&session_id, SessionCommand::SendEncryptedFileChunk {
if global_vars_read.session_manager.send_command(&session_id, SessionCommand::SendEncryptedFileChunk {
ack_sender: ack_sender.clone()
}).await {
print_error!(e);
false
} else {
should_continue
} else {
false
}
}
None => false

View File

@ -1,27 +1,11 @@
mod session;
pub mod protocol;
use std::{collections::HashMap, fs::OpenOptions, io::{self, Write}, net::{IpAddr, SocketAddr}, path::PathBuf, str::from_utf8, sync::{Mutex, RwLock, Arc}};
use tokio::{net::{TcpListener, TcpStream}, sync::mpsc::{self, Sender, Receiver}};
use libmdns::Service;
use strum_macros::Display;
use session::Session;
use ed25519_dalek::PUBLIC_KEY_LENGTH;
use uuid::Uuid;
use platform_dirs::UserDirs;
use crate::{constants, crypto, discovery, identity::{Contact, Identity}, print_error, utils::{get_unix_timestamp, get_not_used_path}};
use async_psec::{PUBLIC_KEY_LENGTH, Session, SessionWriteHalf, PsecWriter, PsecReader, PsecError};
use crate::{constants, protocol, crypto, discovery, identity::{Contact, Identity}, print_error, utils::{get_unix_timestamp, get_not_used_path}};
use crate::ui_interface::UiConnection;
use self::session::{SessionWrite, PSECWriter};
#[derive(Display, Debug, PartialEq, Eq)]
pub enum SessionError {
ConnectionReset,
BrokenPipe,
TransmissionCorrupted,
BufferTooLarge,
InvalidSessionId,
Unknown,
}
pub enum SessionCommand {
Send {
@ -88,7 +72,7 @@ impl SessionManager {
}
}
async fn encrypt_and_send<T: PSECWriter>(&self, writer: &mut T, buff: &[u8]) -> Result<(), SessionError> {
async fn encrypt_and_send<T: PsecWriter>(&self, writer: &mut T, buff: &[u8]) -> Result<(), PsecError> {
let use_padding = self.identity.read().unwrap().as_ref().unwrap().use_padding;
writer.encrypt_and_send(buff, use_padding).await
}
@ -124,17 +108,17 @@ impl SessionManager {
}
}
pub async fn send_command(&self, session_id: &usize, session_command: SessionCommand) -> Result<(), SessionError> {
pub async fn send_command(&self, session_id: &usize, session_command: SessionCommand) -> bool {
if let Some(sender) = self.get_session_sender(session_id) {
match sender.send(session_command).await {
Ok(_) => Ok(()),
Ok(_) => true,
Err(e) => {
print_error!(e);
Err(SessionError::BrokenPipe)
false
}
}
} else {
Err(SessionError::InvalidSessionId)
false
}
}
@ -147,7 +131,7 @@ impl SessionManager {
self.not_seen.write().unwrap().retain(|x| x != session_id);
}
async fn send_msg(&self, session_id: usize, session_write: &mut SessionWrite, buff: &[u8], is_sending: &mut bool, file_ack_sender: &mut Option<Sender<bool>>) -> Result<(), SessionError> {
async fn send_msg(&self, session_id: usize, session_write: &mut SessionWriteHalf, buff: &[u8], is_sending: &mut bool, file_ack_sender: &mut Option<Sender<bool>>) -> Result<(), PsecError> {
self.encrypt_and_send(session_write, &buff).await?;
if buff[0] == protocol::Headers::ACCEPT_LARGE_FILES {
self.sessions.write().unwrap().get_mut(&session_id).unwrap().files_download.as_mut().unwrap().accepted = true;
@ -177,15 +161,16 @@ impl SessionManager {
let mut msg_queue = Vec::new();
let mut is_sending = false;
let (session_read, mut session_write) = session.into_spit().unwrap();
let receiving = session_read.receive_and_decrypt();
let (session_read, mut session_write) = session.into_split().unwrap();
let receiving = session_read.into_receive_and_decrypt();
tokio::pin!(receiving);
loop {
tokio::select! {
result = &mut receiving => {
match result {
Ok((session_read, buffer)) => {
receiving.set(session_read.receive_and_decrypt());
match result.0 {
Ok(buffer) => {
let session_read = result.1;
receiving.set(session_read.into_receive_and_decrypt());
match buffer[0] {
protocol::Headers::ASK_NAME => {
let name = {
@ -378,7 +363,7 @@ impl SessionManager {
}
}
Err(e) => {
if e != SessionError::BrokenPipe && e != SessionError::ConnectionReset && e != SessionError::BufferTooLarge {
if e != PsecError::BrokenPipe && e != PsecError::ConnectionReset && e != PsecError::BufferTooLarge {
print_error!(e);
}
break;
@ -444,7 +429,7 @@ impl SessionManager {
};
match identity {
Some(identity) => {
match session.do_handshake(&identity).await {
match session.do_handshake(&identity.keypair).await {
Ok(_) => {
peer_public_key = session.peer_public_key.unwrap();
if identity.get_public_key() != peer_public_key {
@ -463,7 +448,7 @@ impl SessionManager {
}
};
if let Some(mut session) = session {
let ip = session.get_ip();
let ip = session.get_addr().unwrap().ip();
let mut is_contact = false;
let session_data = {
let mut sessions = session_manager.sessions.write().unwrap();

View File

@ -1,344 +0,0 @@
use std::{convert::TryInto, io::ErrorKind, net::IpAddr};
use tokio::{io::{AsyncReadExt, AsyncWriteExt}, net::{TcpStream, tcp::{OwnedReadHalf, OwnedWriteHalf}}};
use async_trait::async_trait;
use ed25519_dalek;
use ed25519_dalek::{ed25519::signature::Signature, Verifier, PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH};
use x25519_dalek;
use rand_7::{RngCore, rngs::OsRng};
use sha2::{Sha384, Digest};
use aes_gcm::{Aes128Gcm, aead::Aead, NewAead, aead::Payload, Nonce};
use crate::utils::*;
use crate::crypto::*;
use crate::identity::Identity;
use crate::session_manager::SessionError;
use crate::print_error;
const RANDOM_LEN: usize = 64;
const MESSAGE_LEN_LEN: usize = 4;
type MessageLenType = u32;
async fn receive<T: AsyncReadExt + Unpin>(reader: &mut T, buff: &mut [u8]) -> Result<usize, SessionError> {
match reader.read(buff).await {
Ok(read) => {
if read > 0 {
Ok(read)
} else {
Err(SessionError::BrokenPipe)
}
}
Err(e) => {
match e.kind() {
ErrorKind::ConnectionReset => Err(SessionError::ConnectionReset),
_ => {
print_error!("Receive error ({:?}): {}", e.kind(), e);
Err(SessionError::Unknown)
}
}
}
}
}
async fn send<T: AsyncWriteExt + Unpin>(writer: &mut T, buff: &[u8]) -> Result<(), SessionError> {
match writer.write_all(buff).await {
Ok(_) => Ok(()),
Err(e) => Err(match e.kind() {
ErrorKind::BrokenPipe => SessionError::BrokenPipe,
ErrorKind::ConnectionReset => SessionError::ConnectionReset,
_ => {
print_error!("Send error ({:?}): {}", e.kind(), e);
SessionError::Unknown
}
})
}
}
fn pad(plain_text: &[u8], use_padding: bool) -> Vec<u8> {
let encoded_msg_len = (plain_text.len() as MessageLenType).to_be_bytes();
let msg_len = plain_text.len()+encoded_msg_len.len();
let mut output = Vec::from(encoded_msg_len);
if use_padding {
let mut len = 1000;
while len < msg_len {
len *= 2;
}
output.reserve(len);
output.extend(plain_text);
output.resize(len, 0);
OsRng.fill_bytes(&mut output[msg_len..]);
} else {
output.extend(plain_text);
}
output
}
fn unpad(input: Vec<u8>) -> Vec<u8> {
let msg_len = MessageLenType::from_be_bytes(input[0..MESSAGE_LEN_LEN].try_into().unwrap()) as usize;
Vec::from(&input[MESSAGE_LEN_LEN..MESSAGE_LEN_LEN+msg_len])
}
fn encrypt(local_cipher: &Aes128Gcm, local_iv: &[u8], local_counter: &mut usize, plain_text: &[u8], use_padding: bool) -> Vec<u8> {
let padded_msg = pad(plain_text, use_padding);
let cipher_len = (padded_msg.len() as MessageLenType).to_be_bytes();
let payload = Payload {
msg: &padded_msg,
aad: &cipher_len
};
let nonce = iv_to_nonce(local_iv, local_counter);
let cipher_text = local_cipher.encrypt(Nonce::from_slice(&nonce), payload).unwrap();
[&cipher_len, cipher_text.as_slice()].concat()
}
pub async fn encrypt_and_send<T: AsyncWriteExt + Unpin>(writer: &mut T, local_cipher: &Aes128Gcm, local_iv: &[u8], local_counter: &mut usize, plain_text: &[u8], use_padding: bool) -> Result<(), SessionError> {
let cipher_text = encrypt(local_cipher, local_iv, local_counter, plain_text, use_padding);
send(writer, &cipher_text).await
}
#[async_trait]
pub trait PSECWriter {
async fn encrypt_and_send(&mut self, plain_text: &[u8], use_padding: bool) -> Result<(), SessionError>;
fn encrypt(&mut self, plain_text: &[u8], use_padding: bool) -> Vec<u8>;
async fn send(&mut self, cipher_text: &[u8]) -> Result<(), SessionError>;
}
pub struct SessionRead {
read_half: OwnedReadHalf,
peer_cipher: Aes128Gcm,
peer_iv: [u8; IV_LEN],
peer_counter: usize,
}
impl SessionRead {
async fn receive(&mut self, buff: &mut [u8]) -> Result<usize, SessionError> {
receive(&mut self.read_half, buff).await
}
pub async fn receive_and_decrypt(mut self) -> Result<(SessionRead, Vec<u8>), SessionError> {
let mut message_len = [0; MESSAGE_LEN_LEN];
self.receive(&mut message_len).await?;
let recv_len = MessageLenType::from_be_bytes(message_len) as usize + AES_TAG_LEN;
if recv_len <= Session::MAX_RECV_SIZE {
let mut cipher_text = vec![0; recv_len];
let mut read = 0;
while read < recv_len {
read += self.receive(&mut cipher_text[read..]).await?;
}
let peer_nonce = iv_to_nonce(&self.peer_iv, &mut self.peer_counter);
let payload = Payload {
msg: &cipher_text,
aad: &message_len
};
match self.peer_cipher.decrypt(Nonce::from_slice(&peer_nonce), payload) {
Ok(plain_text) => Ok((self, unpad(plain_text))),
Err(_) => Err(SessionError::TransmissionCorrupted)
}
} else {
print_error!("Buffer too large: {} B", recv_len);
Err(SessionError::BufferTooLarge)
}
}
}
pub struct SessionWrite {
write_half: OwnedWriteHalf,
local_cipher: Aes128Gcm,
local_iv: [u8; IV_LEN],
local_counter: usize,
}
#[async_trait]
impl PSECWriter for SessionWrite {
async fn encrypt_and_send(&mut self, plain_text: &[u8], use_padding: bool) -> Result<(), SessionError> {
encrypt_and_send(&mut self.write_half, &self.local_cipher, &self.local_iv, &mut self.local_counter, plain_text, use_padding).await
}
fn encrypt(&mut self, plain_text: &[u8], use_padding: bool) -> Vec<u8> {
encrypt(&self.local_cipher, &self.local_iv, &mut self.local_counter, plain_text, use_padding)
}
async fn send(&mut self, cipher_text: &[u8]) -> Result<(), SessionError> {
send(&mut self.write_half, cipher_text).await
}
}
pub struct Session {
stream: TcpStream,
handshake_sent_buff: Vec<u8>,
handshake_recv_buff: Vec<u8>,
local_cipher: Option<Aes128Gcm>,
local_iv: Option<[u8; IV_LEN]>,
local_counter: usize,
peer_cipher: Option<Aes128Gcm>,
peer_iv: Option<[u8; IV_LEN]>,
peer_counter: usize,
pub peer_public_key: Option<[u8; PUBLIC_KEY_LENGTH]>,
}
impl Session {
const PADDED_MAX_SIZE: usize = 32768000;
const MAX_RECV_SIZE: usize = MESSAGE_LEN_LEN + Session::PADDED_MAX_SIZE + AES_TAG_LEN;
pub fn new(stream: TcpStream) -> Session {
Session {
stream: stream,
handshake_sent_buff: Vec::new(),
handshake_recv_buff: Vec::new(),
local_cipher: None,
local_iv: None,
local_counter: 0,
peer_cipher: None,
peer_iv: None,
peer_counter: 0,
peer_public_key: None,
}
}
pub fn into_spit(self) -> Option<(SessionRead, SessionWrite)> {
let (read_half, write_half) = self.stream.into_split();
Some((
SessionRead {
read_half,
peer_cipher: self.peer_cipher?,
peer_iv: self.peer_iv?,
peer_counter: self.peer_counter,
},
SessionWrite {
write_half,
local_cipher: self.local_cipher?,
local_iv: self.local_iv?,
local_counter: self.local_counter,
}
))
}
pub fn get_ip(&self) -> IpAddr {
self.stream.peer_addr().unwrap().ip()
}
async fn receive(&mut self, buff: &mut [u8]) -> Result<usize, SessionError> {
receive(&mut self.stream, buff).await
}
pub async fn send(&mut self, buff: &[u8]) -> Result<(), SessionError> {
send(&mut self.stream, buff).await
}
async fn handshake_read(&mut self, buff: &mut [u8]) -> Result<(), SessionError> {
self.receive(buff).await?;
self.handshake_recv_buff.extend(buff.as_ref());
Ok(())
}
async fn handshake_write(&mut self, buff: &[u8]) -> Result<(), SessionError> {
self.send(buff).await?;
self.handshake_sent_buff.extend(buff);
Ok(())
}
fn hash_handshake(&self, i_am_bob: bool) -> [u8; 48] {
let handshake_bytes = if i_am_bob {
[self.handshake_sent_buff.as_slice(), self.handshake_recv_buff.as_slice()].concat()
} else {
[self.handshake_recv_buff.as_slice(), self.handshake_sent_buff.as_slice()].concat()
};
let mut hasher = Sha384::new();
hasher.update(handshake_bytes);
let handshake_hash = hasher.finalize();
to_array_48(handshake_hash.as_slice())
}
fn on_handshake_successful(&mut self, application_keys: ApplicationKeys){
self.local_cipher = Some(Aes128Gcm::new_from_slice(&application_keys.local_key).unwrap());
self.local_iv = Some(application_keys.local_iv);
self.peer_cipher = Some(Aes128Gcm::new_from_slice(&application_keys.peer_key).unwrap());
self.peer_iv = Some(application_keys.peer_iv);
self.handshake_sent_buff.clear();
self.handshake_sent_buff.shrink_to_fit();
self.handshake_recv_buff.clear();
self.handshake_recv_buff.shrink_to_fit();
}
pub async fn do_handshake(&mut self, identity: &Identity) -> Result<(), SessionError> {
//ECDHE initial exchange
//generate random bytes
let mut handshake_buffer = [0; RANDOM_LEN+PUBLIC_KEY_LENGTH];
OsRng.fill_bytes(&mut handshake_buffer[..RANDOM_LEN]);
//generate ephemeral x25519 keys
let ephemeral_secret = x25519_dalek::EphemeralSecret::new(OsRng);
let ephemeral_public_key = x25519_dalek::PublicKey::from(&ephemeral_secret);
handshake_buffer[RANDOM_LEN..].copy_from_slice(&ephemeral_public_key.to_bytes());
self.handshake_write(&handshake_buffer).await?;
self.handshake_read(&mut handshake_buffer).await?;
let peer_ephemeral_public_key = x25519_dalek::PublicKey::from(to_array_32(&handshake_buffer[RANDOM_LEN..]));
//calc handshake keys
let i_am_bob = self.handshake_sent_buff < self.handshake_recv_buff; //mutual consensus for keys attribution
let handshake_hash = self.hash_handshake(i_am_bob);
let shared_secret = ephemeral_secret.diffie_hellman(&peer_ephemeral_public_key);
let handshake_keys = HandshakeKeys::derive_keys(shared_secret.to_bytes(), handshake_hash, i_am_bob);
//encrypted handshake
//generate random bytes
let mut random_bytes = [0; RANDOM_LEN];
OsRng.fill_bytes(&mut random_bytes);
self.handshake_write(&random_bytes).await?;
drop(random_bytes);
//receive peer random bytes
let mut peer_random = [0; RANDOM_LEN];
self.handshake_read(&mut peer_random).await?;
drop(peer_random);
//get public key & sign our ephemeral public key
let mut auth_msg = [0; PUBLIC_KEY_LENGTH+SIGNATURE_LENGTH];
auth_msg[..PUBLIC_KEY_LENGTH].copy_from_slice(&identity.get_public_key());
auth_msg[PUBLIC_KEY_LENGTH..].copy_from_slice(&identity.sign(ephemeral_public_key.as_bytes()));
//encrypt auth_msg
let local_cipher = Aes128Gcm::new_from_slice(&handshake_keys.local_key).unwrap();
let mut local_handshake_counter = 0;
let nonce = iv_to_nonce(&handshake_keys.local_iv, &mut local_handshake_counter);
let encrypted_auth_msg = local_cipher.encrypt(Nonce::from_slice(&nonce), auth_msg.as_ref()).unwrap();
self.handshake_write(&encrypted_auth_msg).await?;
let mut encrypted_peer_auth_msg = [0; PUBLIC_KEY_LENGTH+SIGNATURE_LENGTH+AES_TAG_LEN];
self.handshake_read(&mut encrypted_peer_auth_msg).await?;
//decrypt peer_auth_msg
let peer_cipher = Aes128Gcm::new_from_slice(&handshake_keys.peer_key).unwrap();
let mut peer_handshake_counter = 0;
let peer_nonce = iv_to_nonce(&handshake_keys.peer_iv, &mut peer_handshake_counter);
match peer_cipher.decrypt(Nonce::from_slice(&peer_nonce), encrypted_peer_auth_msg.as_ref()) {
Ok(peer_auth_msg) => {
//verify ephemeral public key signature
self.peer_public_key = Some(to_array_32(&peer_auth_msg[..PUBLIC_KEY_LENGTH]));
let peer_public_key = ed25519_dalek::PublicKey::from_bytes(&self.peer_public_key.unwrap()).unwrap();
let peer_signature = Signature::from_bytes(&peer_auth_msg[PUBLIC_KEY_LENGTH..]).unwrap();
if peer_public_key.verify(peer_ephemeral_public_key.as_bytes(), &peer_signature).is_ok() {
let handshake_hash = self.hash_handshake(i_am_bob);
//sending handshake finished
let handshake_finished = compute_handshake_finished(handshake_keys.local_handshake_traffic_secret, handshake_hash);
self.send(&handshake_finished).await?;
let mut peer_handshake_finished = [0; HASH_OUTPUT_LEN];
self.receive(&mut peer_handshake_finished).await?;
if verify_handshake_finished(peer_handshake_finished, handshake_keys.peer_handshake_traffic_secret, handshake_hash) {
//calc application keys
let application_keys = ApplicationKeys::derive_keys(handshake_keys.handshake_secret, handshake_hash, i_am_bob);
self.on_handshake_successful(application_keys);
return Ok(());
}
}
}
Err(_) => {}
}
Err(SessionError::TransmissionCorrupted)
}
}
#[async_trait]
impl PSECWriter for Session {
async fn encrypt_and_send(&mut self, plain_text: &[u8], use_padding: bool) -> Result<(), SessionError> {
encrypt_and_send(&mut self.stream, self.local_cipher.as_ref().unwrap(), self.local_iv.as_ref().unwrap(), &mut self.local_counter, plain_text, use_padding).await
}
fn encrypt(&mut self, plain_text: &[u8], use_padding: bool) -> Vec<u8> {
encrypt(self.local_cipher.as_ref().unwrap(), &self.local_iv.unwrap(), &mut self.local_counter, plain_text, use_padding)
}
async fn send(&mut self, cipher_text: &[u8]) -> Result<(), SessionError> {
send(&mut self.stream, cipher_text).await
}
}

View File

@ -6,7 +6,7 @@ mod ui_messages {
use std::{fmt::Display, iter::FromIterator, net::IpAddr, str::from_utf8};
use tungstenite::Message;
use uuid::Uuid;
use crate::{print_error, session_manager::{LargeFileDownload, LargeFilesDownload, protocol}, utils::to_uuid_bytes};
use crate::{print_error, session_manager::{LargeFileDownload, LargeFilesDownload}, protocol, utils::to_uuid_bytes};
fn simple_event(command: &str, session_id: &usize) -> Message {
Message::from(format!("{} {}", command, session_id))

View File

@ -2,14 +2,6 @@ use std::{convert::TryInto, time::{SystemTime, UNIX_EPOCH}, path::PathBuf};
use uuid::Bytes;
use crate::print_error;
pub fn to_array_48(s: &[u8]) -> [u8; 48] {
s.try_into().unwrap()
}
pub fn to_array_32(s: &[u8]) -> [u8; 32] {
s.try_into().unwrap()
}
pub fn to_uuid_bytes(bytes: &[u8]) -> Option<Bytes> {
match bytes.try_into() {
Ok(uuid) => Some(uuid),