Identity fingerprint in profile pop-up & Peer info pop-up

This commit is contained in:
Matéo Duparc 2021-05-02 16:46:05 +02:00
parent 3681626c48
commit 01e593bbc5
Signed by: hardcoresushi
GPG Key ID: 007F84120107191E
7 changed files with 313 additions and 266 deletions

View File

@ -58,7 +58,7 @@ input[type="file"] {
padding: 20px 70px; padding: 20px 70px;
background-color: #2B2F31; background-color: #2B2F31;
border-radius: 10px; border-radius: 10px;
font-size: 1.3em; font-size: 1.2em;
} }
.popup_background { .popup_background {
height: 100%; height: 100%;
@ -117,6 +117,15 @@ input[type="file"] {
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
} }
.popup .session_info h2 {
text-align: center;
}
.popup .session_info p:first-of-type, .popup .session_info pre {
display: inline-block;
}
.popup .session_info p:nth-of-type(2) {
margin-top: 0;
}
.button_row { .button_row {
display: flex; display: flex;
gap: 15px; gap: 15px;
@ -156,6 +165,13 @@ input[type="file"] {
font-weight: bold; font-weight: bold;
display: inline; display: inline;
} }
#me>div:hover p {
color: var(--accent);
}
#identity_fingerprint {
text-align: center;
margin-bottom: 0;
}
#left_panel ul:last-of-type, #msg_log { #left_panel ul:last-of-type, #msg_log {
flex-grow: 1; flex-grow: 1;
} }
@ -233,6 +249,10 @@ input[type="file"] {
display: flex; display: flex;
align-items: center; align-items: center;
flex-grow: 1; flex-grow: 1;
cursor: pointer;
}
#chat_header>div:hover p {
color: var(--accent);
} }
#chat_header>div>p { #chat_header>div>p {
font-weight: bold; font-weight: bold;

View File

@ -61,6 +61,7 @@
</main> </main>
<script> <script>
//replaced by web server //replaced by web server
let identityFingerprint = "IDENTITY_FINGERPRINT";
let isIdentityProtected = IS_IDENTITY_PROTECTED; let isIdentityProtected = IS_IDENTITY_PROTECTED;
let websocketPort = WEBSOCKET_PORT; let websocketPort = WEBSOCKET_PORT;
</script> </script>

View File

@ -1,7 +1,7 @@
"use strict"; "use strict";
const ENTER_KEY_CODE = 13; const ENTER_KEY_CODE = 13;
let identity_name = undefined; let identityName = undefined;
let socket = null; let socket = null;
let notificationAllowed = false; let notificationAllowed = false;
let currentSessionId = -1; let currentSessionId = -1;
@ -60,7 +60,7 @@ document.getElementById("delete_conversation").onclick = function() {
} }
document.getElementById("add_contact").onclick = function() { document.getElementById("add_contact").onclick = function() {
socket.send("contact "+currentSessionId+" "+sessionsData.get(currentSessionId).name); socket.send("contact "+currentSessionId+" "+sessionsData.get(currentSessionId).name);
sessionsData.get(currentSessionId).is_contact = true; sessionsData.get(currentSessionId).isContact = true;
displayHeader(); displayHeader();
displaySessions(); displaySessions();
} }
@ -78,9 +78,9 @@ document.getElementById("remove_contact").onclick = function() {
button.onclick = function() { button.onclick = function() {
socket.send("uncontact "+currentSessionId); socket.send("uncontact "+currentSessionId);
let session = sessionsData.get(currentSessionId); let session = sessionsData.get(currentSessionId);
session.is_contact = false; session.isContact = false;
session.is_verified = false; session.isVerified = false;
if (!session.is_online) { if (!session.isOnline) {
sessionsData.delete(currentSessionId); sessionsData.delete(currentSessionId);
msgHistory.get(currentSessionId).length = 0; msgHistory.get(currentSessionId).length = 0;
} }
@ -93,7 +93,44 @@ document.getElementById("remove_contact").onclick = function() {
showPopup(mainDiv); showPopup(mainDiv);
} }
document.getElementById("verify").onclick = function() { document.getElementById("verify").onclick = function() {
socket.send("fingerprints "+currentSessionId); let session = sessionsData.get(currentSessionId);
if (typeof session !== "undefined") {
let mainDiv = document.createElement("div");
mainDiv.appendChild(generatePopupWarningTitle());
let instructions = document.createElement("p");
instructions.textContent = "Compare the following fingerprints by a trusted way of communication (such as real life) and be sure they match.";
mainDiv.appendChild(instructions);
let p_local = document.createElement("p");
p_local.textContent = "Local fingerprint:";
mainDiv.appendChild(p_local);
let pre_local = document.createElement("pre");
pre_local.textContent = beautifyFingerprint(identityFingerprint);
mainDiv.appendChild(pre_local);
let p_peer = document.createElement("p");
p_peer.textContent = "Peer fingerprint:";
mainDiv.appendChild(p_peer);
let pre_peer = document.createElement("pre");
pre_peer.textContent = beautifyFingerprint(session.fingerprint);
mainDiv.appendChild(pre_peer);
let buttonRow = document.createElement("div");
buttonRow.classList.add("button_row");
let verifyButton = document.createElement("button");
verifyButton.textContent = "They match";
verifyButton.onclick = function() {
socket.send("verify "+currentSessionId);
sessionsData.get(currentSessionId).isVerified = true;
removePopup();
displayHeader();
displaySessions();
};
buttonRow.appendChild(verifyButton);
let cancelButton = document.createElement("button");
cancelButton.textContent = "They don't match";
cancelButton.onclick = removePopup;
buttonRow.appendChild(cancelButton);
mainDiv.appendChild(buttonRow);
showPopup(mainDiv);
}
} }
document.getElementById("logout").onclick = function() { document.getElementById("logout").onclick = function() {
let mainDiv = document.createElement("div"); let mainDiv = document.createElement("div");
@ -141,125 +178,9 @@ document.getElementById("attach_file").onchange = function(event) {
document.getElementById("file_cancel").onclick = function() { document.getElementById("file_cancel").onclick = function() {
socket.send("abort "+currentSessionId); socket.send("abort "+currentSessionId);
} }
//source: https://stackoverflow.com/a/14919494
function humanFileSize(bytes, dp=1) {
const thresh = 1000;
if (Math.abs(bytes) < thresh) {
return bytes + ' B';
}
const units = ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
let u = -1;
const r = 10**dp;
do {
bytes /= thresh;
++u;
} while (Math.round(Math.abs(bytes) * r) / r >= thresh && u < units.length - 1);
return bytes.toFixed(dp) + ' ' + units[u];
}
//source: https://www.w3schools.com/js/js_cookies.asp
function getCookie(cname) {
var name = cname + "=";
var decodedCookie = decodeURIComponent(document.cookie);
var ca = decodedCookie.split(';');
for(var i = 0; i <ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
}
socket = new WebSocket("ws://"+location.hostname+":"+websocketPort+"/ws");
socket.onopen = function() {
console.log("Connected");
socket.send(getCookie("aira_auth")); //authenticating websocket connection
window.onfocus = function() {
if (currentSessionId != -1) {
socket.send("set_seen "+currentSessionId);
}
}
if (Notification.permission === "granted") {
notificationAllowed = true;
} else if (Notification.permission !== "denied") {
Notification.requestPermission().then(function (permission) {
if (permission === "granted") {
notificationAllowed = true;
}
});
}
};
socket.onmessage = function(msg) {
if (typeof msg.data == "string") {
console.log("Message: "+msg.data);
let args = msg.data.split(" ");
switch (args[0]) {
case "disconnected":
onDisconnected(args[1]);
break;
case "new_session":
onNewSession(args[1], args[2] === "true", msg.data.slice(args[0].length+args[1].length+args[2].length+3));
break;
case "new_message":
onNewMessage(args[1], args[2] === "true", msg.data.slice(args[0].length+args[1].length+args[2].length+3));
break;
case "file":
onFileReceived(args[1], args[2], msg.data.slice(args[0].length+args[1].length+args[2].length+3));
break;
case "file_transfer":
onNewFileTransfer(args[1], args[2], args[3], args[4], args[5], args[6]);
break;
case "ask_large_file":
onAskLargeFile(args[1], args[2], args[3], args[4]);
break;
case "file_accepted":
onFileAccepted(args[1]);
break;
case "aborted":
onFileAborted(args[1]);
break;
case "inc_file_transfer":
onIncFileTransfer(args[1], parseInt(args[2]));
break;
case "load_sent_msg":
onMsgLoad(args[1], args[2] === "true", msg.data.slice(args[0].length+args[1].length+args[2].length+3));
break;
case "load_sent_file":
onFileLoad(args[1], args[2] === "true", args[3], msg.data.slice(args[0].length+args[1].length+args[2].length+args[3].length+4));
break;
case "name_told":
onNameTold(args[1], msg.data.slice(args[0].length+args[1].length+2));
break;
case "is_contact":
onIsContact(args[1], args[2] === "true", msg.data.slice(args[0].length+args[1].length+args[2].length+3));
break;
case "not_seen":
setNotSeen(msg.data.slice(args[0].length+1));
break;
case "fingerprints":
onFingerprints(args[1], args[2]);
break;
case "set_name":
onNameSet(msg.data.slice(args[0].length+1));
break;
case "password_changed":
onPasswordChanged(args[1] === "true", args[2] === "true");
break;
case "logout":
logout();
}
}
}
socket.onclose = function() {
console.log("Disconnected");
}
let msg_log = document.getElementById("msg_log"); let msg_log = document.getElementById("msg_log");
msg_log.onscroll = function() { msg_log.onscroll = function() {
if (sessionsData.get(currentSessionId).is_contact) { if (sessionsData.get(currentSessionId).isContact) {
if (msg_log.scrollTop < 30) { if (msg_log.scrollTop < 30) {
socket.send("load_msgs "+currentSessionId); socket.send("load_msgs "+currentSessionId);
} }
@ -268,14 +189,18 @@ msg_log.onscroll = function() {
let profile_div = document.querySelector("#me>div"); let profile_div = document.querySelector("#me>div");
profile_div.onclick = function() { profile_div.onclick = function() {
let mainDiv = document.createElement("div"); let mainDiv = document.createElement("div");
let avatar = generateAvatar(identity_name); let avatar = generateAvatar(identityName);
mainDiv.appendChild(avatar); mainDiv.appendChild(avatar);
let fingerprint = document.createElement("pre");
fingerprint.id = "identity_fingerprint";
fingerprint.textContent = beautifyFingerprint(identityFingerprint);
mainDiv.appendChild(fingerprint);
let sectionName = document.createElement("section"); let sectionName = document.createElement("section");
sectionName.textContent = "Name:"; sectionName.textContent = "Name:";
let inputName = document.createElement("input"); let inputName = document.createElement("input");
inputName.id = "new_name"; inputName.id = "new_name";
inputName.type = "text"; inputName.type = "text";
inputName.value = identity_name; inputName.value = identityName;
sectionName.appendChild(inputName); sectionName.appendChild(inputName);
let saveNameButton = document.createElement("button"); let saveNameButton = document.createElement("button");
saveNameButton.textContent = "Save"; saveNameButton.textContent = "Save";
@ -370,21 +295,167 @@ profile_div.onclick = function() {
mainDiv.appendChild(sectionDelete); mainDiv.appendChild(sectionDelete);
showPopup(mainDiv); showPopup(mainDiv);
} }
let chatHeader = document.getElementById("chat_header");
chatHeader.children[0].onclick = function() {
let session = sessionsData.get(currentSessionId);
if (typeof session !== "undefined") {
let mainDiv = document.createElement("div");
mainDiv.classList.add("session_info");
mainDiv.appendChild(generateAvatar(session.name));
let h2 = document.createElement("h2");
h2.textContent = session.name;
mainDiv.appendChild(h2);
let pFingerprint = document.createElement("p");
pFingerprint.textContent = "Fingerprint:";
mainDiv.appendChild(pFingerprint);
let pre = document.createElement("pre");
pre.textContent = ' '+beautifyFingerprint(session.fingerprint);
mainDiv.appendChild(pre);
if (session.isOnline) {
let pIp = document.createElement("p");
pIp.textContent = "IP: "+session.ip;
mainDiv.appendChild(pIp);
let pConnection = document.createElement("p");
pConnection.textContent = "Connection: ";
if (session.outgoing) {
pConnection.textContent += "outgoing";
} else {
pConnection.textContent += "incomming";
}
mainDiv.appendChild(pConnection);
}
showPopup(mainDiv);
}
}
document.querySelector("#refresher button").onclick = function() { document.querySelector("#refresher button").onclick = function() {
socket.send("refresh"); socket.send("refresh");
} }
function onNewSession(sessionId, outgoing, name) { //source: https://stackoverflow.com/a/14919494
function humanFileSize(bytes, dp=1) {
const thresh = 1000;
if (Math.abs(bytes) < thresh) {
return bytes + ' B';
}
const units = ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
let u = -1;
const r = 10**dp;
do {
bytes /= thresh;
++u;
} while (Math.round(Math.abs(bytes) * r) / r >= thresh && u < units.length - 1);
return bytes.toFixed(dp) + ' ' + units[u];
}
//source: https://www.w3schools.com/js/js_cookies.asp
function getCookie(cname) {
var name = cname + "=";
var decodedCookie = decodeURIComponent(document.cookie);
var ca = decodedCookie.split(';');
for(var i = 0; i <ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
}
socket = new WebSocket("ws://"+location.hostname+":"+websocketPort+"/ws");
socket.onopen = function() {
console.log("Connected");
socket.send(getCookie("aira_auth")); //authenticating websocket connection
window.onfocus = function() {
if (currentSessionId != -1) {
socket.send("set_seen "+currentSessionId);
}
}
if (Notification.permission === "granted") {
notificationAllowed = true;
} else if (Notification.permission !== "denied") {
Notification.requestPermission().then(function (permission) {
if (permission === "granted") {
notificationAllowed = true;
}
});
}
};
socket.onmessage = function(msg) {
if (typeof msg.data == "string") {
console.log("Message: "+msg.data);
let args = msg.data.split(" ");
switch (args[0]) {
case "disconnected":
onDisconnected(args[1]);
break;
case "new_session":
onNewSession(args[1], args[2] === "true", args[3], args[4], msg.data.slice(args[0].length+args[1].length+args[2].length+args[3].length+args[4].length+5));
break;
case "new_message":
onNewMessage(args[1], args[2] === "true", msg.data.slice(args[0].length+args[1].length+args[2].length+3));
break;
case "file":
onFileReceived(args[1], args[2], msg.data.slice(args[0].length+args[1].length+args[2].length+3));
break;
case "file_transfer":
onNewFileTransfer(args[1], args[2], args[3], args[4], args[5], args[6]);
break;
case "ask_large_file":
onAskLargeFile(args[1], args[2], args[3], args[4]);
break;
case "file_accepted":
onFileAccepted(args[1]);
break;
case "aborted":
onFileAborted(args[1]);
break;
case "inc_file_transfer":
onIncFileTransfer(args[1], parseInt(args[2]));
break;
case "load_sent_msg":
onMsgLoad(args[1], args[2] === "true", msg.data.slice(args[0].length+args[1].length+args[2].length+3));
break;
case "load_sent_file":
onFileLoad(args[1], args[2] === "true", args[3], msg.data.slice(args[0].length+args[1].length+args[2].length+args[3].length+4));
break;
case "name_told":
onNameTold(args[1], msg.data.slice(args[0].length+args[1].length+2));
break;
case "is_contact":
onIsContact(args[1], args[2] === "true", args[3], msg.data.slice(args[0].length+args[1].length+args[2].length+args[3].length+4));
break;
case "not_seen":
setNotSeen(msg.data.slice(args[0].length+1));
break;
case "set_name":
onNameSet(msg.data.slice(args[0].length+1));
break;
case "password_changed":
onPasswordChanged(args[1] === "true", args[2] === "true");
break;
case "logout":
logout();
}
}
}
socket.onclose = function() {
console.log("Disconnected");
}
function onNewSession(sessionId, outgoing, fingerprint, ip, name) {
if (sessionsData.has(sessionId)) { if (sessionsData.has(sessionId)) {
let session = sessionsData.get(sessionId); let session = sessionsData.get(sessionId);
session.is_online = true; session.isOnline = true;
session.outgoing = outgoing; session.outgoing = outgoing;
session.ip = ip;
displaySessions(); displaySessions();
if (currentSessionId == sessionId) { if (currentSessionId == sessionId) {
displayChatBottom(); displayChatBottom();
} }
} else { } else {
addSession(sessionId, name, outgoing, false, false, true); addSession(sessionId, name, outgoing, fingerprint, ip, false, false, true);
} }
} }
function onNameTold(sessionId, name) { function onNameTold(sessionId, name) {
@ -401,14 +472,14 @@ function setNotSeen(str_sessionIds) {
} }
displaySessions(); displaySessions();
} }
function onIsContact(sessionId, verified, name) { function onIsContact(sessionId, verified, fingerprint, name) {
if (sessionsData.has(sessionId)) { if (sessionsData.has(sessionId)) {
let session = sessionsData.get(sessionId); let session = sessionsData.get(sessionId);
session.is_contact = true; session.isContact = true;
session.is_verified = verified; session.isVerified = verified;
onNameTold(sessionId, name); onNameTold(sessionId, name);
} else { } else {
addSession(sessionId, name, true, true, verified, false); addSession(sessionId, name, undefined, fingerprint, undefined, true, verified, false);
} }
} }
function onMsgOrFileReceived(sessionId, outgoing, body) { function onMsgOrFileReceived(sessionId, outgoing, body) {
@ -556,63 +627,20 @@ function onFileLoad(sessionId, outgoing, uuid, fileName) {
function onDisconnected(sessionId) { function onDisconnected(sessionId) {
pendingFiles.delete(sessionId); pendingFiles.delete(sessionId);
let session = sessionsData.get(sessionId); let session = sessionsData.get(sessionId);
if (session.is_contact) { if (session.isContact) {
session.is_online = false; session.isOnline = false;
} else { } else {
sessionsData.delete(sessionId); sessionsData.delete(sessionId);
} }
if (currentSessionId == sessionId) { if (currentSessionId == sessionId) {
displayChatBottom(); displayChatBottom();
} }
if (currentSessionId == sessionId && !session.is_contact) { if (currentSessionId == sessionId && !session.isContact) {
currentSessionId = -1; currentSessionId = -1;
document.getElementById("chat_header").classList.add("offline"); chatHeader.classList.add("offline");
} }
displaySessions(); displaySessions();
} }
function onFingerprints(local, peer) {
let beautifyFingerprints = function(f) {
for (let i=4; i<f.length; i+=5) {
f = f.slice(0, i)+" "+f.slice(i);
}
return f;
};
let mainDiv = document.createElement("div");
mainDiv.appendChild(generatePopupWarningTitle());
let instructions = document.createElement("p");
instructions.textContent = "Compare the following fingerprints by a trusted way of communication (such as real life) and be sure they match.";
mainDiv.appendChild(instructions);
let p_local = document.createElement("p");
p_local.textContent = "Local fingerprint:";
mainDiv.appendChild(p_local);
let pre_local = document.createElement("pre");
pre_local.textContent = beautifyFingerprints(local);
mainDiv.appendChild(pre_local);
let p_peer = document.createElement("p");
p_peer.textContent = "Peer fingerprint:";
mainDiv.appendChild(p_peer);
let pre_peer = document.createElement("pre");
pre_peer.textContent = beautifyFingerprints(peer);
mainDiv.appendChild(pre_peer);
let buttonRow = document.createElement("div");
buttonRow.classList.add("button_row");
let verifyButton = document.createElement("button");
verifyButton.textContent = "They match";
verifyButton.onclick = function() {
socket.send("verify "+currentSessionId);
sessionsData.get(currentSessionId).is_verified = true;
removePopup();
displayHeader();
displaySessions();
};
buttonRow.appendChild(verifyButton);
let cancelButton = document.createElement("button");
cancelButton.textContent = "They don't match";
cancelButton.onclick = removePopup;
buttonRow.appendChild(cancelButton);
mainDiv.appendChild(buttonRow);
showPopup(mainDiv);
}
function onFileReceived(sessionId, uuid, file_name) { function onFileReceived(sessionId, uuid, file_name) {
msgHistory.get(sessionId).push([false, true, [uuid, file_name]]); msgHistory.get(sessionId).push([false, true, [uuid, file_name]]);
onMsgOrFileReceived(sessionId, false, file_name); onMsgOrFileReceived(sessionId, false, file_name);
@ -623,15 +651,15 @@ function onFileSent(sessionId, uuid, file_name) {
dislayHistory(); dislayHistory();
} }
} }
function onNameSet(new_name) { function onNameSet(newName) {
removePopup(); removePopup();
identity_name = new_name; identityName = newName;
displayProfile(); displayProfile();
} }
function onPasswordChanged(success, is_protected) { function onPasswordChanged(success, isProtected) {
if (success) { if (success) {
removePopup(); removePopup();
isIdentityProtected = is_protected; isIdentityProtected = isProtected;
} else { } else {
let input = document.querySelector("input[type=\"password\"]"); let input = document.querySelector("input[type=\"password\"]");
input.value = ""; input.value = "";
@ -640,29 +668,37 @@ function onPasswordChanged(success, is_protected) {
} }
} }
function addSession(sessionId, name, outgoing, is_contact, is_verified, is_online) { function beautifyFingerprint(f) {
for (let i=4; i<f.length; i+=5) {
f = f.slice(0, i)+" "+f.slice(i);
}
return f;
};
function addSession(sessionId, name, outgoing, fingerprint, ip, isContact, isVerified, isOnline) {
sessionsData.set(sessionId, { sessionsData.set(sessionId, {
"name": name, "name": name,
"outgoing": outgoing, "outgoing": outgoing,
"is_contact": is_contact, "fingerprint": fingerprint,
"is_verified": is_verified, "ip": ip,
"isContact": isContact,
"isVerified": isVerified,
"seen": true, "seen": true,
"is_online": is_online, "isOnline": isOnline,
}); });
msgHistory.set(sessionId, []); msgHistory.set(sessionId, []);
displaySessions(); displaySessions();
} }
function displaySessions() { function displaySessions() {
let online_sessions = document.getElementById("online_sessions"); let onlineSessions = document.getElementById("online_sessions");
online_sessions.innerHTML = ""; onlineSessions.innerHTML = "";
let offline_sessions = document.getElementById("offline_sessions"); let offlineSessions = document.getElementById("offline_sessions");
offline_sessions.innerHTML = ""; offlineSessions.innerHTML = "";
sessionsData.forEach(function (session, sessionId) { sessionsData.forEach(function (session, sessionId) {
let session_element = generateSession(sessionId, session); let sessionElement = generateSession(sessionId, session);
if (session.is_online) { if (session.isOnline) {
online_sessions.appendChild(session_element); onlineSessions.appendChild(sessionElement);
} else { } else {
offline_sessions.appendChild(session_element) ; offlineSessions.appendChild(sessionElement) ;
} }
}); });
} }
@ -671,26 +707,25 @@ function logout() {
} }
function displayProfile() { function displayProfile() {
profile_div.innerHTML = ""; profile_div.innerHTML = "";
profile_div.appendChild(generateAvatar(identity_name)); profile_div.appendChild(generateAvatar(identityName));
let p = document.createElement("p"); let p = document.createElement("p");
p.textContent = identity_name; p.textContent = identityName;
profile_div.appendChild(p); profile_div.appendChild(p);
} }
function displayHeader() { function displayHeader() {
let chat_header = document.getElementById("chat_header"); chatHeader.children[0].innerHTML = "";
chat_header.children[0].innerHTML = ""; chatHeader.className = 0;
chat_header.className = 0;
let session = sessionsData.get(currentSessionId); let session = sessionsData.get(currentSessionId);
if (typeof session === "undefined") { if (typeof session === "undefined") {
chat_header.style.display = "none"; chatHeader.style.display = "none";
} else { } else {
chat_header.children[0].appendChild(generateAvatar(session.name)); chatHeader.children[0].appendChild(generateAvatar(session.name));
chat_header.children[0].appendChild(generateName(session.name)); chatHeader.children[0].appendChild(generateName(session.name));
chat_header.style.display = "flex"; chatHeader.style.display = "flex";
if (session.is_contact) { if (session.isContact) {
chat_header.classList.add("is_contact"); chatHeader.classList.add("is_contact");
if (session.is_verified) { if (session.isVerified) {
chat_header.classList.add("is_verified"); chatHeader.classList.add("is_verified");
} }
} }
} }
@ -742,10 +777,10 @@ function generateSession(sessionId, session) {
} else { } else {
li.classList.add("incomming"); li.classList.add("incomming");
} }
if (session.is_contact) { if (session.isContact) {
li.classList.add("is_contact"); li.classList.add("is_contact");
} }
if (session.is_verified) { if (session.isVerified) {
li.classList.add("is_verified"); li.classList.add("is_verified");
} }
if (!session.seen) { if (!session.seen) {
@ -814,7 +849,7 @@ function displayChatBottom(speed = undefined) {
if (typeof session === "undefined") { if (typeof session === "undefined") {
msgBox.removeAttribute("style"); msgBox.removeAttribute("style");
} else { } else {
if (session.is_online) { if (session.isOnline) {
msgBox.style.display = "flex"; msgBox.style.display = "flex";
} else { } else {
msgBox.removeAttribute("style"); msgBox.removeAttribute("style");
@ -873,7 +908,7 @@ function dislayHistory(scrollToBottom = true) {
msgHistory.get(currentSessionId).forEach(entry => { msgHistory.get(currentSessionId).forEach(entry => {
let name; let name;
if (entry[0]) { //outgoing msg if (entry[0]) { //outgoing msg
name = identity_name; name = identityName;
} else { } else {
name = sessionsData.get(currentSessionId).name; name = sessionsData.get(currentSessionId).name;
} }

View File

@ -93,16 +93,25 @@ async fn websocket_worker(mut ui_connection: UiConnection, global_vars: Arc<RwLo
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.get_my_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); 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);
load_msgs(session_manager.clone(), &mut ui_connection, &contact.0); load_msgs(session_manager.clone(), &mut ui_connection, &contact.0);
}); });
session_manager.sessions.read().unwrap().iter().for_each(|session| { session_manager.sessions.read().unwrap().iter().for_each(|session| {
ui_connection.on_new_session(session.0, &session.1.name, session.1.outgoing, session.1.file_download.as_ref()); ui_connection.on_new_session(
session.0,
&session.1.name,
session.1.outgoing,
&crypto::generate_fingerprint(&session.1.peer_public_key),
session.1.ip,
session.1.file_download.as_ref()
);
}); });
let not_seen = session_manager.list_not_seen(); {
let not_seen = session_manager.not_seen.read().unwrap();
if not_seen.len() > 0 { if not_seen.len() > 0 {
ui_connection.set_not_seen(not_seen); ui_connection.set_not_seen(not_seen.clone());
}
} }
session_manager.get_saved_msgs().into_iter().for_each(|msgs| { session_manager.get_saved_msgs().into_iter().for_each(|msgs| {
ui_connection.load_msgs(&msgs.0, &msgs.1); ui_connection.load_msgs(&msgs.0, &msgs.1);
@ -181,13 +190,6 @@ async fn websocket_worker(mut ui_connection: UiConnection, global_vars: Arc<RwLo
Err(e) => print_error!(e) Err(e) => print_error!(e)
} }
} }
"fingerprints" => {
let session_id: usize = args[1].parse().unwrap();
let (local, peer) = session_manager.get_public_keys(&session_id);
let local = crypto::generate_fingerprint(&local);
let peer = crypto::generate_fingerprint(&peer);
ui_connection.fingerprints(&local, &peer);
}
"verify" => { "verify" => {
let session_id: usize = args[1].parse().unwrap(); let session_id: usize = args[1].parse().unwrap();
match session_manager.set_verified(&session_id) { match session_manager.set_verified(&session_id) {
@ -509,9 +511,11 @@ fn index_not_logged_in(global_vars: &Arc<RwLock<GlobalVars>>) -> HttpResponse {
async fn handle_index(req: HttpRequest) -> HttpResponse { async fn handle_index(req: HttpRequest) -> HttpResponse {
let global_vars = req.app_data::<Data<Arc<RwLock<GlobalVars>>>>().unwrap(); let global_vars = req.app_data::<Data<Arc<RwLock<GlobalVars>>>>().unwrap();
if is_authenticated(&req) { if is_authenticated(&req) {
let global_vars_read = global_vars.read().unwrap();
HttpResponse::Ok().body( HttpResponse::Ok().body(
include_str!("frontend/index.html") include_str!("frontend/index.html")
.replace("WEBSOCKET_PORT", &global_vars.read().unwrap().websocket_port.to_string()) .replace("IDENTITY_FINGERPRINT", &crypto::generate_fingerprint(&global_vars_read.session_manager.get_my_public_key()))
.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())
) )
} else { } else {

View File

@ -9,7 +9,7 @@ use session::Session;
use ed25519_dalek::PUBLIC_KEY_LENGTH; use ed25519_dalek::PUBLIC_KEY_LENGTH;
use uuid::Uuid; use uuid::Uuid;
use platform_dirs::UserDirs; use platform_dirs::UserDirs;
use crate::{constants, discovery, identity::{Contact, Identity}, utils::{get_unix_timestamp, get_not_used_path}, print_error}; 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;
#[derive(Display, Debug, PartialEq, Eq)] #[derive(Display, Debug, PartialEq, Eq)]
@ -19,7 +19,7 @@ pub enum SessionError {
TransmissionCorrupted, TransmissionCorrupted,
BufferTooLarge, BufferTooLarge,
InvalidSessionId, InvalidSessionId,
Unknown Unknown,
} }
enum SessionCommand { enum SessionCommand {
@ -55,7 +55,8 @@ pub struct LargeFileDownload {
pub struct SessionData { pub struct SessionData {
pub name: String, pub name: String,
pub outgoing: bool, pub outgoing: bool,
peer_public_key: [u8; PUBLIC_KEY_LENGTH], pub peer_public_key: [u8; PUBLIC_KEY_LENGTH],
pub ip: IpAddr,
sender: Sender<SessionCommand>, sender: Sender<SessionCommand>,
pub file_download: Option<LargeFileDownload>, pub file_download: Option<LargeFileDownload>,
} }
@ -68,7 +69,7 @@ pub struct SessionManager {
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>>,
pub saved_msgs: Mutex<HashMap<usize, Vec<(bool, Vec<u8>)>>>, pub saved_msgs: Mutex<HashMap<usize, Vec<(bool, Vec<u8>)>>>,
not_seen: RwLock<Vec<usize>>, pub not_seen: RwLock<Vec<usize>>,
mdns_service: Mutex<Option<Service>>, mdns_service: Mutex<Option<Service>>,
listener_stop_signal: Mutex<Option<Sender<()>>>, listener_stop_signal: Mutex<Option<Sender<()>>>,
} }
@ -480,6 +481,7 @@ impl SessionManager {
} }
}; };
if let Some(mut session) = session { if let Some(mut session) = session {
let ip = session.get_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();
@ -493,9 +495,10 @@ impl SessionManager {
if is_new_session && session_manager.is_identity_loaded() { //check if we didn't logged out during the handshake if is_new_session && session_manager.is_identity_loaded() { //check if we didn't logged out during the handshake
let (sender, receiver) = mpsc::channel(32); let (sender, receiver) = mpsc::channel(32);
let session_data = SessionData{ let session_data = SessionData{
name: session.get_ip(), name: ip.to_string(),
outgoing, outgoing,
peer_public_key, peer_public_key,
ip,
sender: sender, sender: sender,
file_download: None, file_download: None,
}; };
@ -524,7 +527,7 @@ impl SessionManager {
if let Some(session_data) = session_data { if let Some(session_data) = session_data {
let (session_id, receiver) = session_data; let (session_id, receiver) = session_data;
session_manager.with_ui_connection(|ui_connection| { session_manager.with_ui_connection(|ui_connection| {
ui_connection.on_new_session(&session_id, &session.get_ip(), outgoing, 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.encrypt_and_send(&protocol::ask_name()).await {
@ -567,24 +570,14 @@ impl SessionManager {
Ok(()) Ok(())
} }
pub fn list_contacts(&self) -> Vec<(usize, String, bool)> { pub fn list_contacts(&self) -> Vec<(usize, String, bool, [u8; PUBLIC_KEY_LENGTH])> {
self.loaded_contacts.read().unwrap().iter().map(|c| (*c.0, c.1.name.clone(), c.1.verified)).collect() self.loaded_contacts.read().unwrap().iter().map(|c| (*c.0, c.1.name.clone(), c.1.verified, c.1.public_key)).collect()
} }
pub fn get_saved_msgs(&self) -> HashMap<usize, Vec<(bool, Vec<u8>)>> { pub fn get_saved_msgs(&self) -> HashMap<usize, Vec<(bool, Vec<u8>)>> {
self.saved_msgs.lock().unwrap().clone() self.saved_msgs.lock().unwrap().clone()
} }
pub fn get_peer_public_key(&self, session_id: &usize) -> Option<[u8; PUBLIC_KEY_LENGTH]> {
let sessions = self.sessions.read().unwrap();
let session = sessions.get(session_id)?;
Some(session.peer_public_key)
}
pub fn list_not_seen(&self) -> Vec<usize> {
self.not_seen.read().unwrap().clone()
}
pub fn set_seen(&self, session_id: usize, seen: bool) { pub fn set_seen(&self, session_id: usize, seen: bool) {
let mut not_seen = self.not_seen.write().unwrap(); let mut not_seen = self.not_seen.write().unwrap();
if seen { if seen {
@ -608,7 +601,7 @@ impl SessionManager {
} }
pub fn add_contact(&self, session_id: usize, name: String) -> Result<(), rusqlite::Error> { pub fn add_contact(&self, session_id: usize, name: String) -> Result<(), rusqlite::Error> {
let contact = self.identity.read().unwrap().as_ref().unwrap().add_contact(name, self.get_peer_public_key(&session_id).unwrap())?; let contact = self.identity.read().unwrap().as_ref().unwrap().add_contact(name, self.sessions.read().unwrap().get(&session_id).unwrap().peer_public_key)?;
self.loaded_contacts.write().unwrap().insert(session_id, contact); self.loaded_contacts.write().unwrap().insert(session_id, contact);
self.last_loaded_msg_offsets.write().unwrap().insert(session_id, 0); self.last_loaded_msg_offsets.write().unwrap().insert(session_id, 0);
Ok(()) Ok(())
@ -671,8 +664,8 @@ impl SessionManager {
msgs msgs
} }
pub fn get_public_keys(&self, session_id: &usize) -> ([u8; PUBLIC_KEY_LENGTH], [u8; PUBLIC_KEY_LENGTH]) { pub fn get_my_public_key(&self) -> [u8; PUBLIC_KEY_LENGTH] {
(self.identity.read().unwrap().as_ref().unwrap().get_public_key(), self.loaded_contacts.read().unwrap().get(session_id).unwrap().public_key) self.identity.read().unwrap().as_ref().unwrap().get_public_key()
} }
pub fn get_my_name(&self) -> String { pub fn get_my_name(&self) -> String {

View File

@ -1,4 +1,4 @@
use std::{io::ErrorKind, convert::TryInto}; use std::{convert::TryInto, io::ErrorKind, net::IpAddr};
use tokio::{net::TcpStream, io::{AsyncReadExt, AsyncWriteExt}}; use tokio::{net::TcpStream, io::{AsyncReadExt, AsyncWriteExt}};
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};
@ -48,8 +48,8 @@ impl Session {
} }
} }
pub fn get_ip(&self) -> String { pub fn get_ip(&self) -> IpAddr {
self.stream.peer_addr().unwrap().ip().to_string() self.stream.peer_addr().unwrap().ip()
} }
async fn socket_read(&mut self, buff: &mut [u8]) -> Result<usize, SessionError> { async fn socket_read(&mut self, buff: &mut [u8]) -> Result<usize, SessionError> {

View File

@ -1,9 +1,9 @@
use std::net::TcpStream; use std::net::{IpAddr, TcpStream};
use tungstenite::{WebSocket, protocol::Role, Message}; use tungstenite::{WebSocket, protocol::Role, Message};
use crate::{protocol, session_manager::LargeFileDownload}; use crate::{protocol, session_manager::LargeFileDownload};
mod ui_messages { mod ui_messages {
use std::{iter::FromIterator, str::from_utf8}; use std::{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::{protocol, LargeFileDownload, FileState}, utils::to_uuid_bytes}; use crate::{print_error, session_manager::{protocol, LargeFileDownload, FileState}, utils::to_uuid_bytes};
@ -27,8 +27,8 @@ mod ui_messages {
pub fn on_disconnected(session_id: &usize) -> Message { pub fn on_disconnected(session_id: &usize) -> Message {
simple_event("disconnected", session_id) simple_event("disconnected", session_id)
} }
pub fn on_new_session(session_id: &usize, name: &str, outgoing: bool) -> Message { pub fn on_new_session(session_id: &usize, name: &str, outgoing: bool, fingerprint: &str, ip: IpAddr) -> Message {
Message::from(format!("new_session {} {} {}", session_id, outgoing, name)) Message::from(format!("new_session {} {} {} {} {}", session_id, outgoing, fingerprint, ip, name))
} }
pub fn on_file_received(session_id: &usize, buffer: &[u8]) -> Option<Message> { pub fn on_file_received(session_id: &usize, buffer: &[u8]) -> Option<Message> {
let uuid = Uuid::from_bytes(to_uuid_bytes(&buffer[1..17])?); let uuid = Uuid::from_bytes(to_uuid_bytes(&buffer[1..17])?);
@ -98,11 +98,8 @@ mod ui_messages {
pub fn on_name_told(session_id: &usize, name: &str) -> Message { pub fn on_name_told(session_id: &usize, name: &str) -> Message {
Message::from(format!("name_told {} {}", session_id, name)) Message::from(format!("name_told {} {}", session_id, name))
} }
pub fn set_as_contact(session_id: usize, name: &str, verified: bool) -> Message { pub fn set_as_contact(session_id: usize, name: &str, verified: bool, fingerprint: &str) -> Message {
Message::from(format!("is_contact {} {} {}", session_id, verified, name)) Message::from(format!("is_contact {} {} {} {}", session_id, verified, fingerprint, name))
}
pub fn fingerprints(local: &str, peer: &str) -> Message {
Message::from(format!("fingerprints {} {}", local, peer))
} }
pub fn set_name(new_name: &str) -> Message { pub fn set_name(new_name: &str) -> Message {
Message::from(format!("set_name {}", new_name)) Message::from(format!("set_name {}", new_name))
@ -156,8 +153,8 @@ impl UiConnection {
_ => {} _ => {}
} }
} }
pub fn on_new_session(&mut self, session_id: &usize, name: &str, outgoing: bool, file_transfer: Option<&LargeFileDownload>) { pub fn on_new_session(&mut self, session_id: &usize, name: &str, outgoing: bool, fingerprint: &str, ip: IpAddr, file_transfer: Option<&LargeFileDownload>) {
self.write_message(ui_messages::on_new_session(session_id, name, outgoing)); self.write_message(ui_messages::on_new_session(session_id, name, outgoing, fingerprint, ip));
if let Some(file_transfer) = file_transfer { if let Some(file_transfer) = file_transfer {
self.write_message(ui_messages::new_file_transfer(session_id, file_transfer)); self.write_message(ui_messages::new_file_transfer(session_id, file_transfer));
} }
@ -172,8 +169,8 @@ impl UiConnection {
pub fn inc_file_transfer(&mut self, session_id: &usize, chunk_size: u64) { pub fn inc_file_transfer(&mut self, session_id: &usize, chunk_size: u64) {
self.write_message(ui_messages::inc_file_transfer(session_id, chunk_size)); self.write_message(ui_messages::inc_file_transfer(session_id, chunk_size));
} }
pub fn set_as_contact(&mut self, session_id: usize, name: &str, verified: bool) { pub fn set_as_contact(&mut self, session_id: usize, name: &str, verified: bool, fingerprint: &str) {
self.write_message(ui_messages::set_as_contact(session_id, name, verified)); self.write_message(ui_messages::set_as_contact(session_id, name, verified, fingerprint));
} }
pub fn load_msgs(&mut self, session_id: &usize, msgs: &Vec<(bool, Vec<u8>)>) { pub fn load_msgs(&mut self, session_id: &usize, msgs: &Vec<(bool, Vec<u8>)>) {
msgs.into_iter().rev().for_each(|msg| { msgs.into_iter().rev().for_each(|msg| {
@ -186,9 +183,6 @@ impl UiConnection {
pub fn set_not_seen(&mut self, session_ids: Vec<usize>) { pub fn set_not_seen(&mut self, session_ids: Vec<usize>) {
self.write_message(ui_messages::set_not_seen(session_ids)); self.write_message(ui_messages::set_not_seen(session_ids));
} }
pub fn fingerprints(&mut self, local: &str, peer: &str) {
self.write_message(ui_messages::fingerprints(local, peer));
}
pub fn set_name(&mut self, new_name: &str) { pub fn set_name(&mut self, new_name: &str) {
self.write_message(ui_messages::set_name(new_name)); self.write_message(ui_messages::set_name(new_name));
} }