Optional PSEC padding

This commit is contained in:
Matéo Duparc 2021-05-13 20:28:45 +02:00
parent 861e072537
commit 1c584112b3
Signed by: hardcoresushi
GPG Key ID: 007F84120107191E
9 changed files with 226 additions and 79 deletions

View File

@ -8,6 +8,7 @@ edition = "2018"
rand-8 = {package = "rand", version = "0.8.3"} rand-8 = {package = "rand", version = "0.8.3"}
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"
lazy_static = "1.4" lazy_static = "1.4"
socket2 = "0.4.0" socket2 = "0.4.0"
rusqlite = {version = "0.25.1", features = ["bundled"]} rusqlite = {version = "0.25.1", features = ["bundled"]}

View File

@ -107,9 +107,12 @@ input[type="file"] {
margin: auto; margin: auto;
} }
.popup section { .popup section {
font-weight: bold;
display: block; display: block;
margin-bottom: 20px; margin-bottom: 20px;
border-top: 1px solid black;
}
.popup section:first-of-type {
border-top: unset;
} }
.popup input { .popup input {
display: block; display: block;
@ -128,6 +131,62 @@ input[type="file"] {
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
} }
label {
cursor: pointer;
}
.switch_preference {
display: flex;
align-items: center;
}
.preference_description {
flex-grow: 1;
width: 0; /*fix unknown display bug of .switch*/
margin-right: 20px;
}
.preference_description p:last-of-type {
font-size: .8em;
}
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 34px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.switch span {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
border-radius: 34px;
transition: .3s;
}
.switch span::before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
border-radius: 50%;
transition: .3s;
}
.switch input:checked + span {
background-color: var(--accent);
}
.switch input:focus + span {
box-shadow: 0 0 1px var(--accent);
}
.switch input:checked + span:before {
transform: translateX(26px);
}
#session_info .name { #session_info .name {
display: flex; display: flex;
justify-content: center; justify-content: center;
@ -158,8 +217,6 @@ input[type="file"] {
} }
#right_panel { #right_panel {
background-color: #15191E; background-color: #15191E;
display: flex;
flex-direction: column;
overflow: hidden; overflow: hidden;
} }
#me { #me {

View File

@ -68,6 +68,7 @@
let identityFingerprint = "IDENTITY_FINGERPRINT"; let identityFingerprint = "IDENTITY_FINGERPRINT";
let isIdentityProtected = IS_IDENTITY_PROTECTED; let isIdentityProtected = IS_IDENTITY_PROTECTED;
let websocketPort = WEBSOCKET_PORT; let websocketPort = WEBSOCKET_PORT;
let usePadding = PSEC_PADDING;
</script> </script>
<script src="/static/libs/linkify.min.js"></script> <script src="/static/libs/linkify.min.js"></script>
<script src="/static/libs/linkify-element.min.js"></script> <script src="/static/libs/linkify-element.min.js"></script>

View File

@ -241,7 +241,9 @@ profile_div.onclick = function() {
fingerprint.textContent = beautifyFingerprint(identityFingerprint); fingerprint.textContent = beautifyFingerprint(identityFingerprint);
mainDiv.appendChild(fingerprint); mainDiv.appendChild(fingerprint);
let sectionName = document.createElement("section"); let sectionName = document.createElement("section");
sectionName.textContent = "Name:"; let titleName = document.createElement("h3");
titleName.textContent = "Name:";
sectionName.appendChild(titleName);
let inputName = document.createElement("input"); let inputName = document.createElement("input");
inputName.id = "new_name"; inputName.id = "new_name";
inputName.type = "text"; inputName.type = "text";
@ -255,10 +257,16 @@ profile_div.onclick = function() {
}; };
sectionName.appendChild(saveNameButton); sectionName.appendChild(saveNameButton);
mainDiv.appendChild(sectionName); mainDiv.appendChild(sectionName);
let sectionPadding = document.createElement("section");
sectionPadding.appendChild(generateSwitchPreference("Use PSEC padding", "PSEC padding obfuscates the length of your messages but uses more network bandwidth.", usePadding, function(checked) {
socket.send("set_use_padding "+checked);
usePadding = checked;
}));
mainDiv.appendChild(sectionPadding);
let sectionPassword = document.createElement("section"); let sectionPassword = document.createElement("section");
sectionPassword.textContent = "Change your password:"; let titlePassword = document.createElement("h3");
sectionPassword.style.paddingTop = "1em"; titlePassword.textContent = "Change your password:";
sectionPassword.style.borderTop = "1px solid black"; sectionPassword.appendChild(titlePassword);
if (isIdentityProtected) { if (isIdentityProtected) {
let input_old_password = document.createElement("input"); let input_old_password = document.createElement("input");
input_old_password.type = "password"; input_old_password.type = "password";
@ -313,8 +321,9 @@ profile_div.onclick = function() {
sectionPassword.appendChild(changePasswordButton); sectionPassword.appendChild(changePasswordButton);
mainDiv.appendChild(sectionPassword); mainDiv.appendChild(sectionPassword);
let sectionDelete = document.createElement("section"); let sectionDelete = document.createElement("section");
sectionDelete.textContent = "Delete identity:"; let deleteTitle = document.createElement("h3");
sectionDelete.style.paddingTop = "1em"; deleteTitle.textContent = "Delete identity:";
sectionDelete.appendChild(deleteTitle);
sectionDelete.style.borderTop = "1px solid red"; sectionDelete.style.borderTop = "1px solid red";
let p = document.createElement("p"); let p = document.createElement("p");
p.textContent = "Deleting your identity will delete all your conversations (messages and files), all your contacts, and your private key. You won't be able to be recognized by your contacts anymore."; p.textContent = "Deleting your identity will delete all your conversations (messages and files), all your contacts, and your private key. You won't be able to be recognized by your contacts anymore.";
@ -879,6 +888,32 @@ function generatePopupWarningTitle() {
h2.textContent = "Warning!"; h2.textContent = "Warning!";
return h2; return h2;
} }
function generateSwitchPreference(title, summary, checked, onSwitch) {
let label = document.createElement("label");
label.classList.add("switch_preference");
let divDesc = document.createElement("div");
divDesc.classList.add("preference_description");
let h3 = document.createElement("h3");
h3.textContent = title;
divDesc.appendChild(h3);
let pSummary = document.createElement("p");
pSummary.textContent = summary;
divDesc.appendChild(pSummary);
label.appendChild(divDesc);
let switchDiv = document.createElement("div");
switchDiv.classList.add("switch");
let input = document.createElement("input");
input.type = "checkbox";
input.checked = checked;
input.onchange = function() {
onSwitch(input.checked);
};
switchDiv.appendChild(input);
let span = document.createElement("span");
switchDiv.appendChild(span);
label.appendChild(switchDiv);
return label;
}
function generateName(name) { function generateName(name) {
let p = document.createElement("p"); let p = document.createElement("p");
if (typeof name == "undefined") { if (typeof name == "undefined") {

View File

@ -20,6 +20,7 @@ impl<'a> DBKeys {
pub const KEYPAIR: &'a str = "keypair"; pub const KEYPAIR: &'a str = "keypair";
pub const SALT: &'a str = "salt"; pub const SALT: &'a str = "salt";
pub const MASTER_KEY: &'a str = "master_key"; pub const MASTER_KEY: &'a str = "master_key";
pub const USE_PADDING: &'a str = "use_padding";
} }
fn bool_to_byte(b: bool) -> u8 { fn bool_to_byte(b: bool) -> u8 {
@ -44,7 +45,8 @@ struct EncryptedIdentity {
name: String, name: String,
encrypted_keypair: Vec<u8>, encrypted_keypair: Vec<u8>,
salt: Vec<u8>, salt: Vec<u8>,
encrypted_master_key: Vec<u8> encrypted_master_key: Vec<u8>,
encrypted_use_padding: Vec<u8>,
} }
pub struct Contact { pub struct Contact {
@ -59,6 +61,7 @@ pub struct Identity {
pub name: String, pub name: String,
keypair: Keypair, keypair: Keypair,
pub master_key: [u8; crypto::MASTER_KEY_LEN], pub master_key: [u8; crypto::MASTER_KEY_LEN],
pub use_padding: bool,
} }
impl Identity { impl Identity {
@ -329,6 +332,13 @@ impl Identity {
result result
} }
pub fn set_use_padding(&mut self, use_padding: bool) -> Result<usize, rusqlite::Error> {
self.use_padding = use_padding;
let db = KeyValueTable::new(&get_database_path(), MAIN_TABLE)?;
let encrypted_use_padding = crypto::encrypt_data(&[bool_to_byte(use_padding)], &self.master_key).unwrap();
db.update(DBKeys::USE_PADDING, &encrypted_use_padding)
}
pub fn zeroize(&mut self){ pub fn zeroize(&mut self){
self.master_key.zeroize(); self.master_key.zeroize();
self.keypair.secret.zeroize(); self.keypair.secret.zeroize();
@ -340,11 +350,13 @@ impl Identity {
let encrypted_keypair = db.get(DBKeys::KEYPAIR)?; let encrypted_keypair = db.get(DBKeys::KEYPAIR)?;
let salt = db.get(DBKeys::SALT)?; let salt = db.get(DBKeys::SALT)?;
let encrypted_master_key = db.get(DBKeys::MASTER_KEY)?; let encrypted_master_key = db.get(DBKeys::MASTER_KEY)?;
let encrypted_use_padding = db.get(DBKeys::USE_PADDING)?;
Ok(EncryptedIdentity { Ok(EncryptedIdentity {
name: std::str::from_utf8(&name).unwrap().to_owned(), name: std::str::from_utf8(&name).unwrap().to_owned(),
encrypted_keypair, encrypted_keypair,
salt, salt,
encrypted_master_key, encrypted_master_key,
encrypted_use_padding,
}) })
} }
@ -370,11 +382,20 @@ impl Identity {
}; };
match crypto::decrypt_data(&encrypted_identity.encrypted_keypair, &master_key) { match crypto::decrypt_data(&encrypted_identity.encrypted_keypair, &master_key) {
Ok(keypair) => { Ok(keypair) => {
Ok(Identity{ match crypto::decrypt_data(&encrypted_identity.encrypted_use_padding, &master_key) {
name: encrypted_identity.name, Ok(use_padding) => {
keypair: Keypair::from_bytes(&keypair[..]).unwrap(), Ok(Identity{
master_key: master_key, name: encrypted_identity.name,
}) keypair: Keypair::from_bytes(&keypair[..]).unwrap(),
master_key,
use_padding: byte_to_bool(use_padding[0]).unwrap(),
})
}
Err(e) => {
print_error!(e);
Err(String::from(DATABASE_CORRUPED_ERROR))
}
}
} }
Err(e) => { Err(e) => {
print_error!(e); print_error!(e);
@ -412,10 +433,13 @@ impl Identity {
salt salt
}; };
db.set(DBKeys::SALT, &salt)?; db.set(DBKeys::SALT, &salt)?;
let encrypted_use_padding = crypto::encrypt_data(&[bool_to_byte(true)], &master_key).unwrap();
db.set(DBKeys::USE_PADDING, &encrypted_use_padding)?;
Ok(Identity { Ok(Identity {
name: name.to_owned(), name: name.to_owned(),
keypair, keypair,
master_key, master_key,
use_padding: true,
}) })
} }
@ -470,6 +494,7 @@ impl Clone for Identity {
name: self.name.clone(), name: self.name.clone(),
keypair: Keypair::from_bytes(&self.keypair.to_bytes()).unwrap(), keypair: Keypair::from_bytes(&self.keypair.to_bytes()).unwrap(),
master_key: self.master_key, master_key: self.master_key,
use_padding: self.use_padding,
} }
} }
} }

View File

@ -28,4 +28,7 @@ impl<'a> KeyValueTable<'a> {
pub fn update(&self, key: &str, value: &[u8]) -> Result<usize, Error> { pub fn update(&self, key: &str, value: &[u8]) -> Result<usize, Error> {
self.db.execute(&format!("UPDATE {} SET value=? WHERE key=\"{}\"", self.table_name, key), params![value]) self.db.execute(&format!("UPDATE {} SET value=? WHERE key=\"{}\"", self.table_name, key), params![value])
} }
/*pub fn upsert(&self, key: &str, value: &[u8]) -> Result<usize, Error> {
self.db.execute(&format!("INSERT INTO {} (key, value) VALUES(?1, ?2) ON CONFLICT(key) DO UPDATE SET value=?3", self.table_name), params![key, value, value])
}*/
} }

View File

@ -91,7 +91,7 @@ fn load_msgs(session_manager: Arc<SessionManager>, ui_connection: &mut UiConnect
async fn websocket_worker(mut ui_connection: UiConnection, global_vars: Arc<RwLock<GlobalVars>>, worker_done: Arc<RwLock<bool>>) { async fn websocket_worker(mut ui_connection: UiConnection, global_vars: Arc<RwLock<GlobalVars>>, worker_done: Arc<RwLock<bool>>) {
let session_manager = global_vars.read().unwrap().session_manager.clone(); let session_manager = global_vars.read().unwrap().session_manager.clone();
ui_connection.set_name(&session_manager.get_my_name()); ui_connection.set_name(&session_manager.identity.read().unwrap().as_ref().unwrap().name);
session_manager.list_contacts().into_iter().for_each(|contact|{ session_manager.list_contacts().into_iter().for_each(|contact|{
ui_connection.set_as_contact(contact.0, &contact.1, contact.2, &crypto::generate_fingerprint(&contact.3)); ui_connection.set_as_contact(contact.0, &contact.1, contact.2, &crypto::generate_fingerprint(&contact.3));
session_manager.last_loaded_msg_offsets.write().unwrap().insert(contact.0, 0); session_manager.last_loaded_msg_offsets.write().unwrap().insert(contact.0, 0);
@ -238,6 +238,12 @@ async fn websocket_worker(mut ui_connection: UiConnection, global_vars: Arc<RwLo
print_error!(e); print_error!(e);
} }
} }
"set_use_padding" => {
let use_padding: bool = args[1].parse().unwrap();
if let Err(e) = session_manager.identity.write().unwrap().as_mut().unwrap().set_use_padding(use_padding) {
print_error!(e);
}
}
"change_name" => { "change_name" => {
let new_name = &msg[args[0].len()+1..]; let new_name = &msg[args[0].len()+1..];
match session_manager.change_name(new_name.to_string()).await { match session_manager.change_name(new_name.to_string()).await {
@ -316,7 +322,7 @@ fn handle_load_file(req: HttpRequest, file_info: web::Query<FileInfo>) -> HttpRe
match Uuid::from_str(&file_info.uuid) { match Uuid::from_str(&file_info.uuid) {
Ok(uuid) => { Ok(uuid) => {
let global_vars = req.app_data::<Data<Arc<RwLock<GlobalVars>>>>().unwrap(); let global_vars = req.app_data::<Data<Arc<RwLock<GlobalVars>>>>().unwrap();
match global_vars.read().unwrap().session_manager.load_file(uuid) { match global_vars.read().unwrap().session_manager.identity.read().unwrap().as_ref().unwrap().load_file(uuid) {
Some(buffer) => { Some(buffer) => {
return HttpResponse::Ok().header("Content-Disposition", format!("attachment; filename=\"{}\"", escape_double_quote(html_escape::decode_html_entities(&file_info.file_name).to_string()))).content_type("application/octet-stream").body(buffer); return HttpResponse::Ok().header("Content-Disposition", format!("attachment; filename=\"{}\"", escape_double_quote(html_escape::decode_html_entities(&file_info.file_name).to_string()))).content_type("application/octet-stream").body(buffer);
} }
@ -560,12 +566,15 @@ async fn handle_index(req: HttpRequest) -> HttpResponse {
let html = fs::read_to_string("src/frontend/index.html").unwrap(); let html = fs::read_to_string("src/frontend/index.html").unwrap();
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
let html = include_str!(concat!(env!("OUT_DIR"), "/index.html")); let html = include_str!(concat!(env!("OUT_DIR"), "/index.html"));
let public_key = global_vars_read.session_manager.identity.read().unwrap().as_ref().unwrap().get_public_key();
let use_padding = global_vars_read.session_manager.identity.read().unwrap().as_ref().unwrap().use_padding.to_string();
HttpResponse::Ok().body( HttpResponse::Ok().body(
html html
.replace("AIRA_VERSION", env!("CARGO_PKG_VERSION")) .replace("AIRA_VERSION", env!("CARGO_PKG_VERSION"))
.replace("IDENTITY_FINGERPRINT", &crypto::generate_fingerprint(&global_vars_read.session_manager.get_my_public_key())) .replace("IDENTITY_FINGERPRINT", &crypto::generate_fingerprint(&public_key))
.replace("WEBSOCKET_PORT", &global_vars_read.websocket_port.to_string()) .replace("WEBSOCKET_PORT", &global_vars_read.websocket_port.to_string())
.replace("IS_IDENTITY_PROTECTED", &Identity::is_protected().unwrap().to_string()) .replace("IS_IDENTITY_PROTECTED", &Identity::is_protected().unwrap().to_string())
.replace("PSEC_PADDING", &use_padding)
) )
} else { } else {
index_not_logged_in(global_vars) index_not_logged_in(global_vars)

View File

@ -11,8 +11,7 @@ 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 crate::{constants, 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};
use self::session::SessionWrite;
#[derive(Display, Debug, PartialEq, Eq)] #[derive(Display, Debug, PartialEq, Eq)]
pub enum SessionError { pub enum SessionError {
@ -67,7 +66,7 @@ pub struct SessionData {
pub struct SessionManager { pub struct SessionManager {
session_counter: RwLock<usize>, session_counter: RwLock<usize>,
pub sessions: RwLock<HashMap<usize, SessionData>>, pub sessions: RwLock<HashMap<usize, SessionData>>,
identity: RwLock<Option<Identity>>, pub identity: RwLock<Option<Identity>>,
ui_connection: Mutex<Option<UiConnection>>, ui_connection: Mutex<Option<UiConnection>>,
loaded_contacts: RwLock<HashMap<usize, Contact>>, loaded_contacts: RwLock<HashMap<usize, Contact>>,
pub last_loaded_msg_offsets: RwLock<HashMap<usize, usize>>, pub last_loaded_msg_offsets: RwLock<HashMap<usize, usize>>,
@ -89,6 +88,11 @@ impl SessionManager {
} }
} }
async fn encrypt_and_send<T: PSECWriter>(&self, writer: &mut T, buff: &[u8]) -> Result<(), SessionError> {
let use_padding = self.identity.read().unwrap().as_ref().unwrap().use_padding;
writer.encrypt_and_send(buff, use_padding).await
}
pub async fn connect_to(session_manager: Arc<SessionManager>, ip: IpAddr) -> io::Result<()> { pub async fn connect_to(session_manager: Arc<SessionManager>, ip: IpAddr) -> io::Result<()> {
let stream = TcpStream::connect(SocketAddr::new(ip, constants::PORT)).await?; let stream = TcpStream::connect(SocketAddr::new(ip, constants::PORT)).await?;
SessionManager::handle_new_session(session_manager, Session::new(stream), true); SessionManager::handle_new_session(session_manager, Session::new(stream), true);
@ -143,7 +147,7 @@ impl SessionManager {
} }
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 SessionWrite, buff: &[u8], is_sending: &mut bool, file_ack_sender: &mut Option<Sender<bool>>) -> Result<(), SessionError> {
session_write.encrypt_and_send(&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;
} else if buff[0] == protocol::Headers::ABORT_FILES_TRANSFER { } else if buff[0] == protocol::Headers::ABORT_FILES_TRANSFER {
@ -187,7 +191,7 @@ impl SessionManager {
self.identity.read().unwrap().as_ref().and_then(|identity| Some(identity.name.clone())) self.identity.read().unwrap().as_ref().and_then(|identity| Some(identity.name.clone()))
}; };
if name.is_some() { //can be None if we log out just before locking the identity mutex if name.is_some() { //can be None if we log out just before locking the identity mutex
if let Err(e) = session_write.encrypt_and_send(&protocol::tell_name(&name.unwrap())).await { if let Err(e) = self.encrypt_and_send(&mut session_write, &protocol::tell_name(&name.unwrap())).await {
print_error!(e); print_error!(e);
break; break;
} }
@ -235,7 +239,7 @@ impl SessionManager {
ui_connection.on_ask_large_files(&session_id, &files, download_location.to_str().unwrap()); ui_connection.on_ask_large_files(&session_id, &files, download_location.to_str().unwrap());
}) })
} }
} else if let Err(e) = session_write.encrypt_and_send(&[protocol::Headers::ABORT_FILES_TRANSFER]).await { } else if let Err(e) = self.encrypt_and_send(&mut session_write, &[protocol::Headers::ABORT_FILES_TRANSFER]).await {
print_error!(e); print_error!(e);
break; break;
} }
@ -282,7 +286,7 @@ impl SessionManager {
local_file_handle = None; local_file_handle = None;
} }
} }
if let Err(e) = session_write.encrypt_and_send(&[protocol::Headers::ACK_CHUNK]).await { if let Err(e) = self.encrypt_and_send(&mut session_write, &[protocol::Headers::ACK_CHUNK]).await {
print_error!(e); print_error!(e);
break; break;
} }
@ -297,7 +301,7 @@ impl SessionManager {
if !is_success { if !is_success {
self.sessions.write().unwrap().get_mut(&session_id).unwrap().files_download = None; self.sessions.write().unwrap().get_mut(&session_id).unwrap().files_download = None;
local_file_handle = None; local_file_handle = None;
if let Err(e) = session_write.encrypt_and_send(&[protocol::Headers::ABORT_FILES_TRANSFER]).await { if let Err(e) = self.encrypt_and_send(&mut session_write, &[protocol::Headers::ABORT_FILES_TRANSFER]).await {
print_error!(e); print_error!(e);
break; break;
} }
@ -395,11 +399,11 @@ impl SessionManager {
} }
SessionCommand::EncryptFileChunk { plain_text } => { SessionCommand::EncryptFileChunk { plain_text } => {
last_chunks_sizes.as_mut().unwrap().push(plain_text.len() as u32); last_chunks_sizes.as_mut().unwrap().push(plain_text.len() as u32);
next_chunk = Some(session_write.encrypt(&plain_text)); next_chunk = Some(session_write.encrypt(&plain_text, self.identity.read().unwrap().as_ref().unwrap().use_padding));
} }
SessionCommand::SendEncryptedFileChunk { ack_sender } => { SessionCommand::SendEncryptedFileChunk { ack_sender } => {
if let Some(chunk) = next_chunk.as_ref() { if let Some(chunk) = next_chunk.as_ref() {
match session_write.socket_write(chunk).await { match session_write.send(chunk).await {
Ok(_) => { Ok(_) => {
file_ack_sender = Some(ack_sender); file_ack_sender = Some(ack_sender);
//once the pre-encrypted chunk is sent, we can send the pending messages //once the pre-encrypted chunk is sent, we can send the pending messages
@ -507,7 +511,7 @@ impl SessionManager {
ui_connection.on_new_session(&session_id, &ip.to_string(), outgoing, &crypto::generate_fingerprint(&peer_public_key), ip, None); ui_connection.on_new_session(&session_id, &ip.to_string(), outgoing, &crypto::generate_fingerprint(&peer_public_key), ip, None);
}); });
if !is_contact { if !is_contact {
match session.encrypt_and_send(&protocol::ask_name()).await { match session_manager.encrypt_and_send(&mut session, &protocol::ask_name()).await {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
print_error!(e); print_error!(e);
@ -621,10 +625,6 @@ impl SessionManager {
self.loaded_contacts.read().unwrap().contains_key(session_id) self.loaded_contacts.read().unwrap().contains_key(session_id)
} }
pub fn load_file(&self, uuid: Uuid) -> Option<Vec<u8>> {
self.identity.read().unwrap().as_ref().unwrap().load_file(uuid)
}
pub fn store_file(&self, session_id: &usize, data: &[u8]) -> Result<Uuid, rusqlite::Error> { pub fn store_file(&self, session_id: &usize, data: &[u8]) -> Result<Uuid, rusqlite::Error> {
self.identity.read().unwrap().as_ref().unwrap().store_file(match self.loaded_contacts.read().unwrap().get(session_id) { self.identity.read().unwrap().as_ref().unwrap().store_file(match self.loaded_contacts.read().unwrap().get(session_id) {
Some(contact) => Some(contact.uuid), Some(contact) => Some(contact.uuid),
@ -641,14 +641,6 @@ impl SessionManager {
msgs msgs
} }
pub fn get_my_public_key(&self) -> [u8; PUBLIC_KEY_LENGTH] {
self.identity.read().unwrap().as_ref().unwrap().get_public_key()
}
pub fn get_my_name(&self) -> String {
self.identity.read().unwrap().as_ref().unwrap().name.clone()
}
#[allow(unused_must_use)] #[allow(unused_must_use)]
pub async fn change_name(&self, new_name: String) -> Result<usize, rusqlite::Error> { pub async fn change_name(&self, new_name: String) -> Result<usize, rusqlite::Error> {
let telling_name = protocol::tell_name(&new_name); let telling_name = protocol::tell_name(&new_name);

View File

@ -1,5 +1,6 @@
use std::{convert::TryInto, io::ErrorKind, net::IpAddr}; use std::{convert::TryInto, io::ErrorKind, net::IpAddr};
use tokio::{io::{AsyncReadExt, AsyncWriteExt}, net::{TcpStream, tcp::{OwnedReadHalf, OwnedWriteHalf}}}; use tokio::{io::{AsyncReadExt, AsyncWriteExt}, net::{TcpStream, tcp::{OwnedReadHalf, OwnedWriteHalf}}};
use async_trait::async_trait;
use ed25519_dalek; use ed25519_dalek;
use ed25519_dalek::{ed25519::signature::Signature, Verifier, PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH}; use ed25519_dalek::{ed25519::signature::Signature, Verifier, PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH};
use x25519_dalek; use x25519_dalek;
@ -16,7 +17,7 @@ const RANDOM_LEN: usize = 64;
const MESSAGE_LEN_LEN: usize = 4; const MESSAGE_LEN_LEN: usize = 4;
type MessageLenType = u32; type MessageLenType = u32;
async fn socket_read<T: AsyncReadExt + Unpin>(reader: &mut T, buff: &mut [u8]) -> Result<usize, SessionError> { async fn receive<T: AsyncReadExt + Unpin>(reader: &mut T, buff: &mut [u8]) -> Result<usize, SessionError> {
match reader.read(buff).await { match reader.read(buff).await {
Ok(read) => { Ok(read) => {
if read > 0 { if read > 0 {
@ -37,7 +38,7 @@ async fn socket_read<T: AsyncReadExt + Unpin>(reader: &mut T, buff: &mut [u8]) -
} }
} }
async fn socket_write<T: AsyncWriteExt + Unpin>(writer: &mut T, buff: &[u8]) -> Result<(), SessionError> { async fn send<T: AsyncWriteExt + Unpin>(writer: &mut T, buff: &[u8]) -> Result<(), SessionError> {
match writer.write_all(buff).await { match writer.write_all(buff).await {
Ok(_) => Ok(()), Ok(_) => Ok(()),
Err(e) => Err(match e.kind() { Err(e) => Err(match e.kind() {
@ -51,18 +52,22 @@ async fn socket_write<T: AsyncWriteExt + Unpin>(writer: &mut T, buff: &[u8]) ->
} }
} }
fn pad(plain_text: &[u8]) -> Vec<u8> { fn pad(plain_text: &[u8], use_padding: bool) -> Vec<u8> {
let encoded_msg_len = (plain_text.len() as MessageLenType).to_be_bytes(); let encoded_msg_len = (plain_text.len() as MessageLenType).to_be_bytes();
let msg_len = plain_text.len()+encoded_msg_len.len(); let msg_len = plain_text.len()+encoded_msg_len.len();
let mut len = 1000;
while len < msg_len {
len *= 2;
}
let mut output = Vec::from(encoded_msg_len); let mut output = Vec::from(encoded_msg_len);
output.reserve(len); if use_padding {
output.extend(plain_text); let mut len = 1000;
output.resize(len, 0); while len < msg_len {
OsRng.fill_bytes(&mut output[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 output
} }
@ -71,8 +76,8 @@ fn unpad(input: Vec<u8>) -> Vec<u8> {
Vec::from(&input[MESSAGE_LEN_LEN..MESSAGE_LEN_LEN+msg_len]) 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]) -> Vec<u8> { 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); let padded_msg = pad(plain_text, use_padding);
let cipher_len = (padded_msg.len() as MessageLenType).to_be_bytes(); let cipher_len = (padded_msg.len() as MessageLenType).to_be_bytes();
let payload = Payload { let payload = Payload {
msg: &padded_msg, msg: &padded_msg,
@ -83,9 +88,16 @@ fn encrypt(local_cipher: &Aes128Gcm, local_iv: &[u8], local_counter: &mut usize,
[&cipher_len, cipher_text.as_slice()].concat() [&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]) -> Result<(), SessionError> { 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); let cipher_text = encrypt(local_cipher, local_iv, local_counter, plain_text, use_padding);
socket_write(writer, &cipher_text).await 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 { pub struct SessionRead {
@ -96,19 +108,19 @@ pub struct SessionRead {
} }
impl SessionRead { impl SessionRead {
async fn socket_read(&mut self, buff: &mut [u8]) -> Result<usize, SessionError> { async fn receive(&mut self, buff: &mut [u8]) -> Result<usize, SessionError> {
socket_read(&mut self.read_half, buff).await receive(&mut self.read_half, buff).await
} }
pub async fn receive_and_decrypt(mut self) -> Result<(SessionRead, Vec<u8>), SessionError> { pub async fn receive_and_decrypt(mut self) -> Result<(SessionRead, Vec<u8>), SessionError> {
let mut message_len = [0; MESSAGE_LEN_LEN]; let mut message_len = [0; MESSAGE_LEN_LEN];
self.socket_read(&mut message_len).await?; self.receive(&mut message_len).await?;
let recv_len = MessageLenType::from_be_bytes(message_len) as usize + AES_TAG_LEN; let recv_len = MessageLenType::from_be_bytes(message_len) as usize + AES_TAG_LEN;
if recv_len <= Session::MAX_RECV_SIZE { if recv_len <= Session::MAX_RECV_SIZE {
let mut cipher_text = vec![0; recv_len]; let mut cipher_text = vec![0; recv_len];
let mut read = 0; let mut read = 0;
while read < recv_len { while read < recv_len {
read += self.socket_read(&mut cipher_text[read..]).await?; read += self.receive(&mut cipher_text[read..]).await?;
} }
let peer_nonce = iv_to_nonce(&self.peer_iv, &mut self.peer_counter); let peer_nonce = iv_to_nonce(&self.peer_iv, &mut self.peer_counter);
let payload = Payload { let payload = Payload {
@ -133,15 +145,16 @@ pub struct SessionWrite {
local_counter: usize, local_counter: usize,
} }
impl SessionWrite { #[async_trait]
pub async fn encrypt_and_send(&mut self, plain_text: &[u8]) -> Result<(), SessionError> { impl PSECWriter for SessionWrite {
encrypt_and_send(&mut self.write_half, &self.local_cipher, &self.local_iv, &mut self.local_counter, plain_text).await 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
} }
pub fn encrypt(&mut self, plain_text: &[u8]) -> Vec<u8> { 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) encrypt(&self.local_cipher, &self.local_iv, &mut self.local_counter, plain_text, use_padding)
} }
pub async fn socket_write(&mut self, cipher_text: &[u8]) -> Result<(), SessionError> { async fn send(&mut self, cipher_text: &[u8]) -> Result<(), SessionError> {
socket_write(&mut self.write_half, cipher_text).await send(&mut self.write_half, cipher_text).await
} }
} }
@ -199,22 +212,22 @@ impl Session {
self.stream.peer_addr().unwrap().ip() self.stream.peer_addr().unwrap().ip()
} }
async fn socket_read(&mut self, buff: &mut [u8]) -> Result<usize, SessionError> { async fn receive(&mut self, buff: &mut [u8]) -> Result<usize, SessionError> {
socket_read(&mut self.stream, buff).await receive(&mut self.stream, buff).await
} }
pub async fn socket_write(&mut self, buff: &[u8]) -> Result<(), SessionError> { pub async fn send(&mut self, buff: &[u8]) -> Result<(), SessionError> {
socket_write(&mut self.stream, buff).await send(&mut self.stream, buff).await
} }
async fn handshake_read(&mut self, buff: &mut [u8]) -> Result<(), SessionError> { async fn handshake_read(&mut self, buff: &mut [u8]) -> Result<(), SessionError> {
self.socket_read(buff).await?; self.receive(buff).await?;
self.handshake_recv_buff.extend(buff.as_ref()); self.handshake_recv_buff.extend(buff.as_ref());
Ok(()) Ok(())
} }
async fn handshake_write(&mut self, buff: &[u8]) -> Result<(), SessionError> { async fn handshake_write(&mut self, buff: &[u8]) -> Result<(), SessionError> {
self.socket_write(buff).await?; self.send(buff).await?;
self.handshake_sent_buff.extend(buff); self.handshake_sent_buff.extend(buff);
Ok(()) Ok(())
} }
@ -298,9 +311,9 @@ impl Session {
let handshake_hash = self.hash_handshake(i_am_bob); let handshake_hash = self.hash_handshake(i_am_bob);
//sending handshake finished //sending handshake finished
let handshake_finished = compute_handshake_finished(handshake_keys.local_handshake_traffic_secret, handshake_hash); let handshake_finished = compute_handshake_finished(handshake_keys.local_handshake_traffic_secret, handshake_hash);
self.socket_write(&handshake_finished).await?; self.send(&handshake_finished).await?;
let mut peer_handshake_finished = [0; HASH_OUTPUT_LEN]; let mut peer_handshake_finished = [0; HASH_OUTPUT_LEN];
self.socket_read(&mut peer_handshake_finished).await?; self.receive(&mut peer_handshake_finished).await?;
if verify_handshake_finished(peer_handshake_finished, handshake_keys.peer_handshake_traffic_secret, handshake_hash) { if verify_handshake_finished(peer_handshake_finished, handshake_keys.peer_handshake_traffic_secret, handshake_hash) {
//calc application keys //calc application keys
let application_keys = ApplicationKeys::derive_keys(handshake_keys.handshake_secret, handshake_hash, i_am_bob); let application_keys = ApplicationKeys::derive_keys(handshake_keys.handshake_secret, handshake_hash, i_am_bob);
@ -313,8 +326,19 @@ impl Session {
} }
Err(SessionError::TransmissionCorrupted) Err(SessionError::TransmissionCorrupted)
} }
}
pub async fn encrypt_and_send(&mut self, plain_text: &[u8]) -> 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).await #[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
} }
} }