refactor, decoupling functions + encryption, new key exchqnge every 5 sent messages

This commit is contained in:
Sam Hadow 2025-03-11 13:28:25 +01:00
parent d4e55289a8
commit 49f867fd76
7 changed files with 64 additions and 51 deletions

View File

@ -52,10 +52,9 @@ io.on('connection', async (socket) => {
console.log('User disconnected');
});
socket.on('chat message', async (msg, room, tag, iv, nonce) => {
const roomid = room.substring(5);
const members = await database.getRoomMembers(roomid);
const members = await database.getRoomMembers(room);
members.forEach(memberRoom => {
socket.to(memberRoom).emit('chat message', msg, roomid, tag, iv, nonce, session.publicKey);
socket.to(memberRoom).emit('chat message', msg, room, tag, iv, nonce, session.publicKey);
});
console.log('message: ' + msg + ', sender: ' + session.id);
console.log(session.publicKey);

View File

@ -19,4 +19,37 @@ function splitIntoChunks(data) {
return chunks;
}
export { concatUint8Arrays, splitIntoChunks };
/*
Convert an ArrayBuffer into a string
from https://developers.google.com/web/updates/2012/06/How-to-convert-ArrayBuffer-to-and-from-String
*/
function ab2str(buf) {
return String.fromCharCode.apply(null, new Uint8Array(buf));
}
/*
Convert a string into an ArrayBuffer
from https://developers.google.com/web/updates/2012/06/How-to-convert-ArrayBuffer-to-and-from-String
*/
function str2ab(str) {
const buf = new ArrayBuffer(str.length);
const bufView = new Uint8Array(buf);
for (let i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
function generateRandomUint8Array(length = 16) {
const randomArray = new Uint8Array(length);
window.crypto.getRandomValues(randomArray);
return randomArray;
}
const fromHexString = (hexString) =>
Uint8Array.from(hexString.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));
const toHexString = (bytes) =>
bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '');
export { concatUint8Arrays, splitIntoChunks, ab2str, str2ab, fromHexString, toHexString, generateRandomUint8Array };

View File

@ -2,6 +2,7 @@ import { genKeys, sharedKey } from "./ecdh.js";
import { keccakAEAD } from "./aead.js";
import { keccakKDF } from "./kdf.js";
import { render_room, render_rooms_wrapper } from "./rooms.js";
import { fromHexString, toHexString, generateRandomUint8Array } from "./arrayutils.js";
const socket = io();
let secret = null;
@ -9,13 +10,15 @@ let sharedsecret = {};
let dh_ratchets = {};
let sending_ratchets = {};
let receiving_ratchets = {};
let messages_exchanged = {};
const max_messages_number = 5;
render_rooms_wrapper();
function init_ratchets(order, user_pubkey) {
let dh_ratchet = new keccakKDF()
let key_1 = dh_ratchet.init(sharedsecret[user_pubkey], new Uint8Array(0));
let key_2 = dh_ratchet.next(new Uint8Array(0));
let key_2 = dh_ratchet.next();
dh_ratchets[user_pubkey] = dh_ratchet;
let sending_ratchet = new keccakKDF();
let receiving_ratchet = new keccakKDF();
@ -31,7 +34,7 @@ function init_ratchets(order, user_pubkey) {
}
sending_ratchets[user_pubkey] = sending_ratchet;
receiving_ratchets[user_pubkey] = receiving_ratchet;
messages_exchanged[user_pubkey] = 0;
}
socket.on('chat message', (msg, room, tag_received, iv, nonce, pubkey_received) => {
@ -55,6 +58,10 @@ socket.on('chat message', (msg, room, tag_received, iv, nonce, pubkey_received)
});
socket.on('key exchange', (user_pubkey, pubkey, part) => {
key_exchange(user_pubkey, pubkey, part);
});
function key_exchange(user_pubkey, pubkey, part) {
let keys = null;
switch (part) {
case 0:
@ -76,7 +83,14 @@ socket.on('key exchange', (user_pubkey, pubkey, part) => {
init_ratchets(1, user_pubkey);
break;
}
});
}
function reset_ratchets(user_pubkey) {
if (messages_exchanged[user_pubkey] >= max_messages_number) {
key_exchange(user_pubkey, null, 0);
messages_exchanged[user_pubkey] = 0;
}
}
export function create_listener(form, input, messages) {
form.addEventListener('submit', function(e) {
@ -86,8 +100,10 @@ export function create_listener(form, input, messages) {
window.scrollTo(0, document.body.scrollHeight);
const pubkey = Array.from(form.classList).find(className => className.startsWith('key-')).replace('key-', '');
let {cipher, tag, iv, nonce} = encrypt_message(input.value, pubkey);
socket.emit('chat message', cipher, form.id, tag, iv, nonce);
socket.emit('chat message', cipher, form.id.substring(5), tag, iv, nonce);
input.value = '';
messages_exchanged[pubkey] += 1;
reset_ratchets(pubkey);
}
});
}
@ -115,12 +131,6 @@ function decrypt_message(cipher, user_pubkey, iv, nonce, associated_data) {
};
}
function generateRandomUint8Array(length = 16) {
const randomArray = new Uint8Array(length);
window.crypto.getRandomValues(randomArray);
return randomArray;
}
function append_message(type, text, messageUl) {
const item = document.createElement('li');
const bubble = document.createElement('div');
@ -143,12 +153,6 @@ function append_message(type, text, messageUl) {
messageUl.appendChild(item);
}
const fromHexString = (hexString) =>
Uint8Array.from(hexString.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));
const toHexString = (bytes) =>
bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '');
export async function reconnectSocket() {
socket.disconnect();
console.log("Socket disconnected.");

View File

@ -1,23 +1,4 @@
/*
Convert an ArrayBuffer into a string
from https://developers.google.com/web/updates/2012/06/How-to-convert-ArrayBuffer-to-and-from-String
*/
export function ab2str(buf) {
return String.fromCharCode.apply(null, new Uint8Array(buf));
}
/*
Convert a string into an ArrayBuffer
from https://developers.google.com/web/updates/2012/06/How-to-convert-ArrayBuffer-to-and-from-String
*/
export function str2ab(str) {
const buf = new ArrayBuffer(str.length);
const bufView = new Uint8Array(buf);
for (let i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
import {ab2str, str2ab} from './arrayutils.js';
export function exportedKeyToPem(key, type) {
let exportedAsString = ab2str(key);

View File

@ -1,5 +1,5 @@
import { ab2str, exportedKeyToPem, pemToKey, genKey } from "./ecc.js";
import { exportedKeyToPem, pemToKey, genKey } from "./ecc.js";
import { ab2str } from "./arrayutils.js"
import { add_div_register_text } from "./registertext.js";
export async function registerConfirm() {

View File

@ -7,17 +7,12 @@ html(lang="en-US")
link(rel="stylesheet" href="/style.css")
script(src="/socket.io/socket.io.js", defer)
script(src="/script.js", defer)
script(type="module", src="/ecc.js", defer)
if isLoggedIn
script(type="module", src="/chat.js", defer)
script(src="/pubkey.js", defer)
script(src="/noble-curves.js", defer)
script(type="module", src="/rooms.js", defer)
script(type="module", src="/ecdh.js", defer)
script(src="/pubkey.js", defer)
script(type="module", src="/popups-logged.js", defer)
script(type="module", src="/keccak.js", defer)
script(type="module", src="/aead.js", defer)
script(type="module", src="/kdf.js", defer)
script(type="module", src="/rooms.js", defer)
script(type="module", src="/chat.js", defer)
else
script(type="module", src="/popups.js", defer)
script(type="module", src="/register.js", defer)

View File

@ -1,4 +1,5 @@
import { ab2str, str2ab, exportedKeyToPem, pemToKey, genKey } from '../src/public/ecc.js';
import { exportedKeyToPem, pemToKey, genKey } from '../src/public/ecc.js';
import { ab2str, str2ab } from '../src/public/arrayutils.js';
describe('ecc.js functions', () => {