@@ 8,3 8,64 @@
border-radius: .5em;
}
+ul#chat-room {
+ margin: 0;
+ padding: 2em;
+}
+
+ul#chat-room li {
+ position: relative;
+ margin: 0.5em 4em 0 0;
+ padding: 0.5em;
+ border-radius: 1.25em;
+ list-style: none;
+ word-break: break-word;
+ background-color: green;
+}
+
+ul#chat-room li::before {
+ position: absolute;
+ width: 0;
+ height: 0;
+ content: '';
+ bottom: -0.75em;
+ left: -0.75em;
+ transform: rotate(90deg);
+ border-top: 1.5em solid transparent;
+ border-bottom: 1.5em solid transparent;
+ border-right: 1.5em solid green;
+}
+
+ul#chat-room li.own {
+ background-color: blue;
+ margin: 0.5em 0 0 4em;
+}
+
+ul#chat-room li.own::before {
+ display: none;
+}
+
+ul#chat-room li.own::after {
+ position: absolute;
+ width: 0;
+ height: 0;
+ content: '';
+ bottom: -0.75em;
+ right: -0.75em;
+ transform: rotate(90deg);
+ border-top: 1.5em solid transparent;
+ border-bottom: 1.5em solid transparent;
+ border-right: 1.5em solid blue;
+}
+
+input#chat-input {
+ width: 88%;
+ padding: 0.5em;
+ border-radius: 1.25em;
+}
+
+button#chat-button {
+ width: 9%;
+ padding: 0.5em;
+ border-radius: 1.25em;
+}
@@ 3,6 3,13 @@ const ivSize = 12; // size for initial value array per AES GCM specific
const iterationNum = 10000; // lowest recommendable number of iterations for password derivation
const sigSize = 4096; // recommended size for digital signatures
+// message authenticated enum
+const MessageAuthenticated = {
+ True: "True",
+ False: "False",
+ Self: "Self"
+}
+
// conversion functions
function arrayBufferToArray(buf) {
return new Uint8Array(buf);
@@ 82,16 89,20 @@ async function encryptAndSign(str) {
};
async function verify(sig, arr) {
try {
- for (let i = 0; i < keychain.length; i++) {
+ if (1 <= keychain.length) {
+ const trust = await window.crypto.subtle.verify("RSASSA-PKCS1-v1_5", keychain[0], sig, arr);
+ if (trust === true) { return "Self"; }
+ }
+ for (let i = 1; i < keychain.length; i++) {
let trust = await window.crypto.subtle.verify("RSASSA-PKCS1-v1_5", keychain[i], sig, arr);
- if (trust === true) { return true; }
+ if (trust === true) { return "True"; }
}
console.log("could not verify signature");
- return false;
+ return "False";
} catch(e) {
console.log("verification failed")
- return false;
+ return "False";
}
}
async function decrypt(passkey, salt, iv, arr) {
@@ 109,8 120,8 @@ async function justVerify(blob) {
const sig = arr.slice(saltSize+ivSize, saltSize+ivSize+(sigSize/8));
const msg = arr.slice(saltSize+ivSize+(sigSize/8));
- await verify(arrayToArrayBuffer(sig), arrayToArrayBuffer(msg));
- return arrayToString(msg);
+ const trust = await verify(arrayToArrayBuffer(sig), arrayToArrayBuffer(msg));
+ return { "trust": trust, "message": arrayToString(msg) };
}
async function verifyAndDecrypt(blob) {
const arr = base64ToArray(blob);
@@ 119,8 130,9 @@ async function verifyAndDecrypt(blob) {
const sig = arr.slice(saltSize+ivSize, saltSize+ivSize+(sigSize/8));
const msg = arr.slice(saltSize+ivSize+(sigSize/8));
- await verify(sig, msg);
- return await decrypt(passkey, salt, iv, msg);
+ const trust = await verify(sig, msg);
+ const message = await decrypt(passkey, salt, iv, msg);
+ return { "trust": trust, "message": message };
}
function escapeHTML(str) {
return str.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('"', '"').replaceAll("'", ''');
@@ 158,13 170,20 @@ function connect() {
socket.onmessage = async (m) => {
const el = document.createElement('li');
+
+ var verified;
if (passkey == null) {
- const msg = await justVerify(m.data);
- el.innerHTML = escapeHTML(msg);
+ verified = await justVerify(m.data);
} else {
- const decrypted = await verifyAndDecrypt(m.data);
- el.innerHTML = escapeHTML(decrypted);
+ verified = await verifyAndDecrypt(m.data);
}
+
+ if (verified.trust == MessageAuthenticated.Self) {
+ el.classList.add('own');
+ }
+
+ el.innerHTML = escapeHTML(verified.message);
+
document.getElementById('chat-room').appendChild(el);
};
};