Optional PSEC padding
This commit is contained in:
parent
861e072537
commit
1c584112b3
@ -8,6 +8,7 @@ edition = "2018"
|
||||
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"
|
||||
lazy_static = "1.4"
|
||||
socket2 = "0.4.0"
|
||||
rusqlite = {version = "0.25.1", features = ["bundled"]}
|
||||
|
@ -107,9 +107,12 @@ input[type="file"] {
|
||||
margin: auto;
|
||||
}
|
||||
.popup section {
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
margin-bottom: 20px;
|
||||
border-top: 1px solid black;
|
||||
}
|
||||
.popup section:first-of-type {
|
||||
border-top: unset;
|
||||
}
|
||||
.popup input {
|
||||
display: block;
|
||||
@ -128,6 +131,62 @@ input[type="file"] {
|
||||
display: inline-block;
|
||||
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 {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@ -158,8 +217,6 @@ input[type="file"] {
|
||||
}
|
||||
#right_panel {
|
||||
background-color: #15191E;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
#me {
|
||||
|
@ -68,6 +68,7 @@
|
||||
let identityFingerprint = "IDENTITY_FINGERPRINT";
|
||||
let isIdentityProtected = IS_IDENTITY_PROTECTED;
|
||||
let websocketPort = WEBSOCKET_PORT;
|
||||
let usePadding = PSEC_PADDING;
|
||||
</script>
|
||||
<script src="/static/libs/linkify.min.js"></script>
|
||||
<script src="/static/libs/linkify-element.min.js"></script>
|
||||
|
@ -241,7 +241,9 @@ profile_div.onclick = function() {
|
||||
fingerprint.textContent = beautifyFingerprint(identityFingerprint);
|
||||
mainDiv.appendChild(fingerprint);
|
||||
let sectionName = document.createElement("section");
|
||||
sectionName.textContent = "Name:";
|
||||
let titleName = document.createElement("h3");
|
||||
titleName.textContent = "Name:";
|
||||
sectionName.appendChild(titleName);
|
||||
let inputName = document.createElement("input");
|
||||
inputName.id = "new_name";
|
||||
inputName.type = "text";
|
||||
@ -255,10 +257,16 @@ profile_div.onclick = function() {
|
||||
};
|
||||
sectionName.appendChild(saveNameButton);
|
||||
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");
|
||||
sectionPassword.textContent = "Change your password:";
|
||||
sectionPassword.style.paddingTop = "1em";
|
||||
sectionPassword.style.borderTop = "1px solid black";
|
||||
let titlePassword = document.createElement("h3");
|
||||
titlePassword.textContent = "Change your password:";
|
||||
sectionPassword.appendChild(titlePassword);
|
||||
if (isIdentityProtected) {
|
||||
let input_old_password = document.createElement("input");
|
||||
input_old_password.type = "password";
|
||||
@ -313,8 +321,9 @@ profile_div.onclick = function() {
|
||||
sectionPassword.appendChild(changePasswordButton);
|
||||
mainDiv.appendChild(sectionPassword);
|
||||
let sectionDelete = document.createElement("section");
|
||||
sectionDelete.textContent = "Delete identity:";
|
||||
sectionDelete.style.paddingTop = "1em";
|
||||
let deleteTitle = document.createElement("h3");
|
||||
deleteTitle.textContent = "Delete identity:";
|
||||
sectionDelete.appendChild(deleteTitle);
|
||||
sectionDelete.style.borderTop = "1px solid red";
|
||||
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.";
|
||||
@ -879,6 +888,32 @@ function generatePopupWarningTitle() {
|
||||
h2.textContent = "Warning!";
|
||||
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) {
|
||||
let p = document.createElement("p");
|
||||
if (typeof name == "undefined") {
|
||||
|
@ -20,6 +20,7 @@ impl<'a> DBKeys {
|
||||
pub const KEYPAIR: &'a str = "keypair";
|
||||
pub const SALT: &'a str = "salt";
|
||||
pub const MASTER_KEY: &'a str = "master_key";
|
||||
pub const USE_PADDING: &'a str = "use_padding";
|
||||
}
|
||||
|
||||
fn bool_to_byte(b: bool) -> u8 {
|
||||
@ -44,7 +45,8 @@ struct EncryptedIdentity {
|
||||
name: String,
|
||||
encrypted_keypair: Vec<u8>,
|
||||
salt: Vec<u8>,
|
||||
encrypted_master_key: Vec<u8>
|
||||
encrypted_master_key: Vec<u8>,
|
||||
encrypted_use_padding: Vec<u8>,
|
||||
}
|
||||
|
||||
pub struct Contact {
|
||||
@ -59,6 +61,7 @@ pub struct Identity {
|
||||
pub name: String,
|
||||
keypair: Keypair,
|
||||
pub master_key: [u8; crypto::MASTER_KEY_LEN],
|
||||
pub use_padding: bool,
|
||||
}
|
||||
|
||||
impl Identity {
|
||||
@ -329,6 +332,13 @@ impl Identity {
|
||||
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){
|
||||
self.master_key.zeroize();
|
||||
self.keypair.secret.zeroize();
|
||||
@ -340,11 +350,13 @@ impl Identity {
|
||||
let encrypted_keypair = db.get(DBKeys::KEYPAIR)?;
|
||||
let salt = db.get(DBKeys::SALT)?;
|
||||
let encrypted_master_key = db.get(DBKeys::MASTER_KEY)?;
|
||||
let encrypted_use_padding = db.get(DBKeys::USE_PADDING)?;
|
||||
Ok(EncryptedIdentity {
|
||||
name: std::str::from_utf8(&name).unwrap().to_owned(),
|
||||
encrypted_keypair,
|
||||
salt,
|
||||
encrypted_master_key,
|
||||
encrypted_use_padding,
|
||||
})
|
||||
}
|
||||
|
||||
@ -370,11 +382,20 @@ impl Identity {
|
||||
};
|
||||
match crypto::decrypt_data(&encrypted_identity.encrypted_keypair, &master_key) {
|
||||
Ok(keypair) => {
|
||||
Ok(Identity{
|
||||
name: encrypted_identity.name,
|
||||
keypair: Keypair::from_bytes(&keypair[..]).unwrap(),
|
||||
master_key: master_key,
|
||||
})
|
||||
match crypto::decrypt_data(&encrypted_identity.encrypted_use_padding, &master_key) {
|
||||
Ok(use_padding) => {
|
||||
Ok(Identity{
|
||||
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) => {
|
||||
print_error!(e);
|
||||
@ -412,10 +433,13 @@ impl Identity {
|
||||
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 {
|
||||
name: name.to_owned(),
|
||||
keypair,
|
||||
master_key,
|
||||
use_padding: true,
|
||||
})
|
||||
}
|
||||
|
||||
@ -470,6 +494,7 @@ impl Clone for Identity {
|
||||
name: self.name.clone(),
|
||||
keypair: Keypair::from_bytes(&self.keypair.to_bytes()).unwrap(),
|
||||
master_key: self.master_key,
|
||||
use_padding: self.use_padding,
|
||||
}
|
||||
}
|
||||
}
|
@ -28,4 +28,7 @@ impl<'a> KeyValueTable<'a> {
|
||||
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])
|
||||
}
|
||||
/*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])
|
||||
}*/
|
||||
}
|
15
src/main.rs
15
src/main.rs
@ -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>>) {
|
||||
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|{
|
||||
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);
|
||||
@ -238,6 +238,12 @@ async fn websocket_worker(mut ui_connection: UiConnection, global_vars: Arc<RwLo
|
||||
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" => {
|
||||
let new_name = &msg[args[0].len()+1..];
|
||||
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) {
|
||||
Ok(uuid) => {
|
||||
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) => {
|
||||
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();
|
||||
#[cfg(not(debug_assertions))]
|
||||
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(
|
||||
html
|
||||
.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("IS_IDENTITY_PROTECTED", &Identity::is_protected().unwrap().to_string())
|
||||
.replace("PSEC_PADDING", &use_padding)
|
||||
)
|
||||
} else {
|
||||
index_not_logged_in(global_vars)
|
||||
|
@ -11,8 +11,7 @@ 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 crate::ui_interface::UiConnection;
|
||||
|
||||
use self::session::SessionWrite;
|
||||
use self::session::{SessionWrite, PSECWriter};
|
||||
|
||||
#[derive(Display, Debug, PartialEq, Eq)]
|
||||
pub enum SessionError {
|
||||
@ -67,7 +66,7 @@ pub struct SessionData {
|
||||
pub struct SessionManager {
|
||||
session_counter: RwLock<usize>,
|
||||
pub sessions: RwLock<HashMap<usize, SessionData>>,
|
||||
identity: RwLock<Option<Identity>>,
|
||||
pub identity: RwLock<Option<Identity>>,
|
||||
ui_connection: Mutex<Option<UiConnection>>,
|
||||
loaded_contacts: RwLock<HashMap<usize, Contact>>,
|
||||
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<()> {
|
||||
let stream = TcpStream::connect(SocketAddr::new(ip, constants::PORT)).await?;
|
||||
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> {
|
||||
session_write.encrypt_and_send(&buff).await?;
|
||||
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;
|
||||
} 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()))
|
||||
};
|
||||
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);
|
||||
break;
|
||||
}
|
||||
@ -235,7 +239,7 @@ impl SessionManager {
|
||||
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);
|
||||
break;
|
||||
}
|
||||
@ -282,7 +286,7 @@ impl SessionManager {
|
||||
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);
|
||||
break;
|
||||
}
|
||||
@ -297,7 +301,7 @@ impl SessionManager {
|
||||
if !is_success {
|
||||
self.sessions.write().unwrap().get_mut(&session_id).unwrap().files_download = 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);
|
||||
break;
|
||||
}
|
||||
@ -395,11 +399,11 @@ impl SessionManager {
|
||||
}
|
||||
SessionCommand::EncryptFileChunk { plain_text } => {
|
||||
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 } => {
|
||||
if let Some(chunk) = next_chunk.as_ref() {
|
||||
match session_write.socket_write(chunk).await {
|
||||
match session_write.send(chunk).await {
|
||||
Ok(_) => {
|
||||
file_ack_sender = Some(ack_sender);
|
||||
//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);
|
||||
});
|
||||
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(_) => {}
|
||||
Err(e) => {
|
||||
print_error!(e);
|
||||
@ -621,10 +625,6 @@ impl SessionManager {
|
||||
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> {
|
||||
self.identity.read().unwrap().as_ref().unwrap().store_file(match self.loaded_contacts.read().unwrap().get(session_id) {
|
||||
Some(contact) => Some(contact.uuid),
|
||||
@ -641,14 +641,6 @@ impl SessionManager {
|
||||
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)]
|
||||
pub async fn change_name(&self, new_name: String) -> Result<usize, rusqlite::Error> {
|
||||
let telling_name = protocol::tell_name(&new_name);
|
||||
|
@ -1,5 +1,6 @@
|
||||
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;
|
||||
@ -16,7 +17,7 @@ const RANDOM_LEN: usize = 64;
|
||||
const MESSAGE_LEN_LEN: usize = 4;
|
||||
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 {
|
||||
Ok(read) => {
|
||||
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 {
|
||||
Ok(_) => Ok(()),
|
||||
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 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);
|
||||
output.reserve(len);
|
||||
output.extend(plain_text);
|
||||
output.resize(len, 0);
|
||||
OsRng.fill_bytes(&mut output[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
|
||||
}
|
||||
|
||||
@ -71,8 +76,8 @@ fn unpad(input: Vec<u8>) -> Vec<u8> {
|
||||
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> {
|
||||
let padded_msg = pad(plain_text);
|
||||
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,
|
||||
@ -83,9 +88,16 @@ fn encrypt(local_cipher: &Aes128Gcm, local_iv: &[u8], local_counter: &mut usize,
|
||||
[&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> {
|
||||
let cipher_text = encrypt(local_cipher, local_iv, local_counter, plain_text);
|
||||
socket_write(writer, &cipher_text).await
|
||||
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 {
|
||||
@ -96,19 +108,19 @@ pub struct SessionRead {
|
||||
}
|
||||
|
||||
impl SessionRead {
|
||||
async fn socket_read(&mut self, buff: &mut [u8]) -> Result<usize, SessionError> {
|
||||
socket_read(&mut self.read_half, buff).await
|
||||
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.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;
|
||||
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.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 payload = Payload {
|
||||
@ -133,15 +145,16 @@ pub struct SessionWrite {
|
||||
local_counter: usize,
|
||||
}
|
||||
|
||||
impl SessionWrite {
|
||||
pub async fn encrypt_and_send(&mut self, plain_text: &[u8]) -> Result<(), SessionError> {
|
||||
encrypt_and_send(&mut self.write_half, &self.local_cipher, &self.local_iv, &mut self.local_counter, plain_text).await
|
||||
#[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
|
||||
}
|
||||
pub fn encrypt(&mut self, plain_text: &[u8]) -> Vec<u8> {
|
||||
encrypt(&self.local_cipher, &self.local_iv, &mut self.local_counter, plain_text)
|
||||
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)
|
||||
}
|
||||
pub async fn socket_write(&mut self, cipher_text: &[u8]) -> Result<(), SessionError> {
|
||||
socket_write(&mut self.write_half, cipher_text).await
|
||||
async fn send(&mut self, cipher_text: &[u8]) -> Result<(), SessionError> {
|
||||
send(&mut self.write_half, cipher_text).await
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,22 +212,22 @@ impl Session {
|
||||
self.stream.peer_addr().unwrap().ip()
|
||||
}
|
||||
|
||||
async fn socket_read(&mut self, buff: &mut [u8]) -> Result<usize, SessionError> {
|
||||
socket_read(&mut self.stream, buff).await
|
||||
async fn receive(&mut self, buff: &mut [u8]) -> Result<usize, SessionError> {
|
||||
receive(&mut self.stream, buff).await
|
||||
}
|
||||
|
||||
pub async fn socket_write(&mut self, buff: &[u8]) -> Result<(), SessionError> {
|
||||
socket_write(&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.socket_read(buff).await?;
|
||||
self.receive(buff).await?;
|
||||
self.handshake_recv_buff.extend(buff.as_ref());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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);
|
||||
Ok(())
|
||||
}
|
||||
@ -298,9 +311,9 @@ impl Session {
|
||||
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.socket_write(&handshake_finished).await?;
|
||||
self.send(&handshake_finished).await?;
|
||||
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) {
|
||||
//calc application keys
|
||||
let application_keys = ApplicationKeys::derive_keys(handshake_keys.handshake_secret, handshake_hash, i_am_bob);
|
||||
@ -313,8 +326,19 @@ impl Session {
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user