Switching to async-psec
This commit is contained in:
parent
8adcef8852
commit
275c2972df
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,3 +1,2 @@
|
|||||||
Cargo.lock
|
/target
|
||||||
target
|
|
||||||
local
|
local
|
2754
Cargo.lock
generated
Normal file
2754
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
47
Cargo.toml
47
Cargo.toml
@ -5,39 +5,36 @@ authors = ["Hardcore Sushi <hardcore.sushi@disroot.org>"]
|
|||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rand-8 = {package = "rand", version = "0.8.3"}
|
rand = "0.8"
|
||||||
rand-7 = {package = "rand", version = "0.7.3"}
|
rand-7 = { package = "rand", version = "0.7.3" }
|
||||||
tokio = {version = "1", features = ["rt", "rt-multi-thread", "macros", "net", "io-util"]}
|
tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros", "net", "io-util"] }
|
||||||
async-trait = "0.1.5"
|
async-psec = { version = "0.1", git = "https://forge.chapril.org/hardcoresushi/async-psec", features = ["split"] }
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
socket2 = "0.4.0"
|
socket2 = "0.4"
|
||||||
rusqlite = {version = "0.25.1", features = ["bundled"]}
|
rusqlite = { version = "0.25.1", features = ["bundled"] }
|
||||||
ed25519-dalek = "1" #for singing
|
ed25519-dalek = "1" #for singing
|
||||||
x25519-dalek = "1.1" #for shared secret
|
sha2 = "0.9"
|
||||||
sha2 = "0.9.3"
|
aes-gcm = "0.9"
|
||||||
hkdf = "0.11.0"
|
aes-gcm-siv = "0.10" #Database
|
||||||
aes-gcm = "0.9.0" #PSEC
|
hkdf = "0.11"
|
||||||
aes-gcm-siv = "0.10.0" #Database
|
hex = "0.4"
|
||||||
hmac = "0.11.0"
|
|
||||||
hex = "0.4.3"
|
|
||||||
strum_macros = "0.20.1" #display enums
|
|
||||||
actix-web = "3"
|
actix-web = "3"
|
||||||
actix-multipart = "0.3"
|
actix-multipart = "0.3"
|
||||||
time = "0.2.25" #needed for actix cookies
|
time = "0.2" #needed for actix cookies
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
tungstenite = "0.13.0" #websocket
|
tungstenite = "0.13" #websocket
|
||||||
serde = "1.0.124" #serialization
|
serde = "1.0" #serialization
|
||||||
html-escape = "0.2.7"
|
html-escape = "0.2"
|
||||||
sanitize-filename = "0.3"
|
sanitize-filename = "0.3"
|
||||||
platform-dirs = "0.3.0"
|
platform-dirs = "0.3"
|
||||||
uuid = {version = "0.8", features = ["v4"]}
|
uuid = { version = "0.8", features = ["v4"] }
|
||||||
webbrowser = "0.5.5"
|
webbrowser = "0.5"
|
||||||
libmdns = "0.6" #mDNS advertiser
|
libmdns = "0.6" #mDNS advertiser
|
||||||
multicast_dns = "0.5" #mDNS browser
|
multicast_dns = "0.5" #mDNS browser
|
||||||
if-addrs = "0.6"
|
if-addrs = "0.6"
|
||||||
base64 = "0.13.0"
|
base64 = "0.13"
|
||||||
scrypt = "0.7.0"
|
scrypt = "0.7"
|
||||||
zeroize = "1.2.0"
|
zeroize = "1.2"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
html-minifier = "3.0.11"
|
html-minifier = "3.0"
|
||||||
|
146
src/crypto.rs
146
src/crypto.rs
@ -1,148 +1,18 @@
|
|||||||
use std::convert::TryInto;
|
use std::{convert::TryInto, fmt::Display};
|
||||||
use hkdf::Hkdf;
|
use hkdf::Hkdf;
|
||||||
use sha2::Sha384;
|
use sha2::Sha384;
|
||||||
use hmac::{Hmac, Mac, NewMac};
|
|
||||||
use scrypt::{scrypt, Params};
|
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::{aead::Aead, NewAead, Nonce};
|
||||||
use aes_gcm_siv::Aes256GcmSiv;
|
use aes_gcm_siv::Aes256GcmSiv;
|
||||||
use zeroize::Zeroize;
|
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 IV_LEN: usize = 12;
|
||||||
pub const AES_TAG_LEN: usize = 16;
|
pub const AES_TAG_LEN: usize = 16;
|
||||||
pub const SALT_LEN: usize = 32;
|
pub const SALT_LEN: usize = 32;
|
||||||
const PASSWORD_HASH_LEN: usize = 32;
|
const PASSWORD_HASH_LEN: usize = 32;
|
||||||
pub const MASTER_KEY_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 {
|
pub fn generate_fingerprint(public_key: &[u8]) -> String {
|
||||||
let mut raw_fingerprint = [0; 16];
|
let mut raw_fingerprint = [0; 16];
|
||||||
Hkdf::<Sha384>::new(None, public_key).expand(&[], &mut raw_fingerprint).unwrap();
|
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] {
|
pub fn generate_master_key() -> [u8; MASTER_KEY_LEN] {
|
||||||
let mut master_key = [0; MASTER_KEY_LEN];
|
let mut master_key = [0; MASTER_KEY_LEN];
|
||||||
OsRng.fill_bytes(&mut master_key);
|
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)
|
Ok(cipher_text)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Display, Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum CryptoError {
|
pub enum CryptoError {
|
||||||
DecryptionFailed,
|
DecryptionFailed,
|
||||||
InvalidLength
|
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> {
|
pub fn decrypt_data(data: &[u8], master_key: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
||||||
if data.len() <= IV_LEN || master_key.len() != MASTER_KEY_LEN {
|
if data.len() <= IV_LEN || master_key.len() != MASTER_KEY_LEN {
|
||||||
return Err(CryptoError::InvalidLength);
|
return Err(CryptoError::InvalidLength);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use crypto::CryptoError;
|
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 rusqlite::{Connection, params};
|
||||||
use platform_dirs::AppDirs;
|
use platform_dirs::AppDirs;
|
||||||
use utils::to_uuid_bytes;
|
use utils::to_uuid_bytes;
|
||||||
@ -59,17 +59,13 @@ pub struct Contact {
|
|||||||
|
|
||||||
pub struct Identity {
|
pub struct Identity {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
keypair: Keypair,
|
pub keypair: Keypair,
|
||||||
pub master_key: [u8; crypto::MASTER_KEY_LEN],
|
pub master_key: [u8; crypto::MASTER_KEY_LEN],
|
||||||
pub use_padding: bool,
|
pub use_padding: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Identity {
|
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] {
|
pub fn get_public_key(&self) -> [u8; PUBLIC_KEY_LENGTH] {
|
||||||
self.keypair.public.to_bytes()
|
self.keypair.public.to_bytes()
|
||||||
}
|
}
|
||||||
|
53
src/main.rs
53
src/main.rs
@ -2,6 +2,7 @@ mod key_value_table;
|
|||||||
mod identity;
|
mod identity;
|
||||||
mod crypto;
|
mod crypto;
|
||||||
mod session_manager;
|
mod session_manager;
|
||||||
|
mod protocol;
|
||||||
mod utils;
|
mod utils;
|
||||||
mod ui_interface;
|
mod ui_interface;
|
||||||
mod constants;
|
mod constants;
|
||||||
@ -13,14 +14,14 @@ use actix_web::{App, HttpMessage, HttpRequest, HttpResponse, HttpServer, http::{
|
|||||||
use actix_multipart::Multipart;
|
use actix_multipart::Multipart;
|
||||||
use tungstenite::Message;
|
use tungstenite::Message;
|
||||||
use futures::{StreamExt, TryStreamExt};
|
use futures::{StreamExt, TryStreamExt};
|
||||||
use rand_8::{RngCore, rngs::OsRng};
|
use rand::{RngCore, rngs::OsRng};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use platform_dirs::AppDirs;
|
use platform_dirs::AppDirs;
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
use utils::escape_double_quote;
|
use utils::escape_double_quote;
|
||||||
use identity::Identity;
|
use identity::Identity;
|
||||||
use session_manager::{SessionManager, SessionCommand, protocol};
|
use session_manager::{SessionManager, SessionCommand};
|
||||||
use ui_interface::UiConnection;
|
use ui_interface::UiConnection;
|
||||||
|
|
||||||
async fn start_websocket_server(global_vars: Arc<RwLock<GlobalVars>>) -> u16 {
|
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" => {
|
"send" => {
|
||||||
let session_id: usize = args[1].parse().unwrap();
|
let session_id: usize = args[1].parse().unwrap();
|
||||||
let buffer = protocol::new_message(msg[args[0].len()+args[1].len()+2..].to_string());
|
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()
|
buff: buffer.clone()
|
||||||
}).await {
|
}).await {
|
||||||
Ok(_) => session_manager.store_msg(&session_id, true, buffer),
|
session_manager.store_msg(&session_id, true, buffer);
|
||||||
Err(e) => print_error!(e)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"large_files" => {
|
"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) {
|
for n in (2..args.len()).step_by(2) {
|
||||||
file_info.push((args[n].parse::<u64>().unwrap(), base64::decode(args[n+1]).unwrap()));
|
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)
|
buff: protocol::ask_large_files(file_info)
|
||||||
}).await {
|
}).await;
|
||||||
print_error!(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
"download" => {
|
"download" => {
|
||||||
let session_id: usize = args[1].parse().unwrap();
|
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]
|
buff: vec![protocol::Headers::ACCEPT_LARGE_FILES]
|
||||||
}).await {
|
}).await;
|
||||||
print_error!(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
"abort" => {
|
"abort" => {
|
||||||
let session_id: usize = args[1].parse().unwrap();
|
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]
|
buff: vec![protocol::Headers::ABORT_FILES_TRANSFER]
|
||||||
}).await {
|
}).await;
|
||||||
print_error!(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
"sending_ended" => {
|
"sending_ended" => {
|
||||||
let session_id: usize = args[1].parse().unwrap();
|
let session_id: usize = args[1].parse().unwrap();
|
||||||
if let Err(e) = session_manager.send_command(&session_id, SessionCommand::SendingEnded).await {
|
session_manager.send_command(&session_id, SessionCommand::SendingEnded).await;
|
||||||
print_error!(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
"load_msgs" => {
|
"load_msgs" => {
|
||||||
let session_id: usize = args[1].parse().unwrap();
|
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" => {
|
"ask_name" => {
|
||||||
let session_id: usize = args[1].parse().unwrap();
|
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()
|
buff: protocol::ask_name()
|
||||||
}).await {
|
}).await;
|
||||||
print_error!(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
"set_use_padding" => {
|
"set_use_padding" => {
|
||||||
let use_padding: bool = args[1].parse().unwrap();
|
let use_padding: bool = args[1].parse().unwrap();
|
||||||
@ -357,10 +347,9 @@ async fn handle_send_file(req: HttpRequest, mut payload: Multipart) -> HttpRespo
|
|||||||
while let Some(Ok(chunk)) = field.next().await {
|
while let Some(Ok(chunk)) = field.next().await {
|
||||||
buffer.extend(chunk);
|
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)
|
buff: protocol::file(filename, &buffer)
|
||||||
}).await {
|
}).await {
|
||||||
Ok(_) => {
|
|
||||||
match global_vars_read.session_manager.store_file(&session_id, &buffer) {
|
match global_vars_read.session_manager.store_file(&session_id, &buffer) {
|
||||||
Ok(file_uuid) => {
|
Ok(file_uuid) => {
|
||||||
let msg = [&[protocol::Headers::FILE][..], file_uuid.as_bytes(), filename.as_bytes()].concat();
|
let msg = [&[protocol::Headers::FILE][..], file_uuid.as_bytes(), filename.as_bytes()].concat();
|
||||||
@ -370,8 +359,6 @@ async fn handle_send_file(req: HttpRequest, mut payload: Multipart) -> HttpRespo
|
|||||||
Err(e) => print_error!(e)
|
Err(e) => print_error!(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => print_error!(e)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
let (ack_sender, mut ack_receiver) = mpsc::channel(1);
|
let (ack_sender, mut ack_receiver) = mpsc::channel(1);
|
||||||
let mut pending_buffer = Vec::new();
|
let mut pending_buffer = Vec::new();
|
||||||
@ -394,22 +381,20 @@ async fn handle_send_file(req: HttpRequest, mut payload: Multipart) -> HttpRespo
|
|||||||
break;
|
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()
|
plain_text: chunk_buffer.clone()
|
||||||
}).await {
|
}).await {
|
||||||
print_error!(e);
|
|
||||||
return HttpResponse::InternalServerError().finish();
|
return HttpResponse::InternalServerError().finish();
|
||||||
}
|
}
|
||||||
if !match ack_receiver.recv().await {
|
if !match ack_receiver.recv().await {
|
||||||
Some(should_continue) => {
|
Some(should_continue) => {
|
||||||
//send previous encrypted chunk even if transfert is aborted to keep PSEC nonces syncrhonized
|
//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()
|
ack_sender: ack_sender.clone()
|
||||||
}).await {
|
}).await {
|
||||||
print_error!(e);
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
should_continue
|
should_continue
|
||||||
|
} else {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => false
|
None => false
|
||||||
|
@ -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 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 tokio::{net::{TcpListener, TcpStream}, sync::mpsc::{self, Sender, Receiver}};
|
||||||
use libmdns::Service;
|
use libmdns::Service;
|
||||||
use strum_macros::Display;
|
|
||||||
use session::Session;
|
|
||||||
use ed25519_dalek::PUBLIC_KEY_LENGTH;
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use platform_dirs::UserDirs;
|
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 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 {
|
pub enum SessionCommand {
|
||||||
Send {
|
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;
|
let use_padding = self.identity.read().unwrap().as_ref().unwrap().use_padding;
|
||||||
writer.encrypt_and_send(buff, use_padding).await
|
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) {
|
if let Some(sender) = self.get_session_sender(session_id) {
|
||||||
match sender.send(session_command).await {
|
match sender.send(session_command).await {
|
||||||
Ok(_) => Ok(()),
|
Ok(_) => true,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
print_error!(e);
|
print_error!(e);
|
||||||
Err(SessionError::BrokenPipe)
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(SessionError::InvalidSessionId)
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,7 +131,7 @@ impl SessionManager {
|
|||||||
self.not_seen.write().unwrap().retain(|x| x != session_id);
|
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?;
|
self.encrypt_and_send(session_write, &buff).await?;
|
||||||
if buff[0] == protocol::Headers::ACCEPT_LARGE_FILES {
|
if buff[0] == protocol::Headers::ACCEPT_LARGE_FILES {
|
||||||
self.sessions.write().unwrap().get_mut(&session_id).unwrap().files_download.as_mut().unwrap().accepted = true;
|
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 msg_queue = Vec::new();
|
||||||
let mut is_sending = false;
|
let mut is_sending = false;
|
||||||
|
|
||||||
let (session_read, mut session_write) = session.into_spit().unwrap();
|
let (session_read, mut session_write) = session.into_split().unwrap();
|
||||||
let receiving = session_read.receive_and_decrypt();
|
let receiving = session_read.into_receive_and_decrypt();
|
||||||
tokio::pin!(receiving);
|
tokio::pin!(receiving);
|
||||||
loop {
|
loop {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
result = &mut receiving => {
|
result = &mut receiving => {
|
||||||
match result {
|
match result.0 {
|
||||||
Ok((session_read, buffer)) => {
|
Ok(buffer) => {
|
||||||
receiving.set(session_read.receive_and_decrypt());
|
let session_read = result.1;
|
||||||
|
receiving.set(session_read.into_receive_and_decrypt());
|
||||||
match buffer[0] {
|
match buffer[0] {
|
||||||
protocol::Headers::ASK_NAME => {
|
protocol::Headers::ASK_NAME => {
|
||||||
let name = {
|
let name = {
|
||||||
@ -378,7 +363,7 @@ impl SessionManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
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);
|
print_error!(e);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -444,7 +429,7 @@ impl SessionManager {
|
|||||||
};
|
};
|
||||||
match identity {
|
match identity {
|
||||||
Some(identity) => {
|
Some(identity) => {
|
||||||
match session.do_handshake(&identity).await {
|
match session.do_handshake(&identity.keypair).await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
peer_public_key = session.peer_public_key.unwrap();
|
peer_public_key = session.peer_public_key.unwrap();
|
||||||
if identity.get_public_key() != peer_public_key {
|
if identity.get_public_key() != peer_public_key {
|
||||||
@ -463,7 +448,7 @@ impl SessionManager {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
if let Some(mut session) = session {
|
if let Some(mut session) = session {
|
||||||
let ip = session.get_ip();
|
let ip = session.get_addr().unwrap().ip();
|
||||||
let mut is_contact = false;
|
let mut is_contact = false;
|
||||||
let session_data = {
|
let session_data = {
|
||||||
let mut sessions = session_manager.sessions.write().unwrap();
|
let mut sessions = session_manager.sessions.write().unwrap();
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,7 +6,7 @@ mod ui_messages {
|
|||||||
use std::{fmt::Display, iter::FromIterator, net::IpAddr, str::from_utf8};
|
use std::{fmt::Display, iter::FromIterator, net::IpAddr, str::from_utf8};
|
||||||
use tungstenite::Message;
|
use tungstenite::Message;
|
||||||
use uuid::Uuid;
|
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 {
|
fn simple_event(command: &str, session_id: &usize) -> Message {
|
||||||
Message::from(format!("{} {}", command, session_id))
|
Message::from(format!("{} {}", command, session_id))
|
||||||
|
@ -2,14 +2,6 @@ use std::{convert::TryInto, time::{SystemTime, UNIX_EPOCH}, path::PathBuf};
|
|||||||
use uuid::Bytes;
|
use uuid::Bytes;
|
||||||
use crate::print_error;
|
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> {
|
pub fn to_uuid_bytes(bytes: &[u8]) -> Option<Bytes> {
|
||||||
match bytes.try_into() {
|
match bytes.try_into() {
|
||||||
Ok(uuid) => Some(uuid),
|
Ok(uuid) => Some(uuid),
|
||||||
|
Loading…
Reference in New Issue
Block a user