2025-11-12 10:13:24 -05:00
|
|
|
import { postData } from "./client.js";
|
|
|
|
|
|
|
|
|
|
const loginForm = document.getElementById("loginForm");
|
|
|
|
|
|
2025-11-28 13:54:57 -05:00
|
|
|
const error = document.querySelector(".error");
|
|
|
|
|
const errorMessage = document.querySelector(".error p");
|
|
|
|
|
const success = document.querySelector(".success");
|
|
|
|
|
|
2026-03-09 16:31:40 -04:00
|
|
|
openKeyDB();
|
|
|
|
|
|
2025-11-12 10:13:24 -05:00
|
|
|
if (loginForm) loginForm.onsubmit = async e => {
|
|
|
|
|
e.preventDefault();
|
2026-03-09 16:31:40 -04:00
|
|
|
|
|
|
|
|
let saltObject = await postData("/getSalt", {
|
|
|
|
|
username: document.getElementById("logUser").value
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (saltObject.message == "User not found") {
|
|
|
|
|
errorMessage.innerHTML = saltObject.message;
|
|
|
|
|
error.style.display = "flex";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { authenticationKeyString, encryptionKeyObject } = await generateKeys(document.getElementById("logPass").value, hexToUint8Array(saltObject.salt));
|
|
|
|
|
|
|
|
|
|
saveEncryptionKey(encryptionKeyObject);
|
|
|
|
|
|
2025-11-12 10:13:24 -05:00
|
|
|
let resultObject = await postData("/login", {
|
|
|
|
|
username: document.getElementById("logUser").value,
|
2026-03-09 16:31:40 -04:00
|
|
|
password: authenticationKeyString
|
2025-11-12 10:13:24 -05:00
|
|
|
});
|
2025-11-28 13:54:57 -05:00
|
|
|
if (resultObject.message.includes("success")) {
|
|
|
|
|
error.style.display = "none";
|
|
|
|
|
success.style.display = "flex";
|
|
|
|
|
} else {
|
|
|
|
|
errorMessage.innerHTML = resultObject.message;
|
|
|
|
|
error.style.display = "flex";
|
|
|
|
|
}
|
2025-11-12 10:13:24 -05:00
|
|
|
if (resultObject.success) {
|
2025-11-18 21:16:35 -05:00
|
|
|
window.location.href = "/home"
|
2025-11-12 10:13:24 -05:00
|
|
|
localStorage.setItem("token", resultObject.token);
|
|
|
|
|
}
|
|
|
|
|
};
|
2026-03-09 16:31:40 -04:00
|
|
|
|
|
|
|
|
async function generateKeys(password, salt) {
|
|
|
|
|
const encoder = new TextEncoder();
|
|
|
|
|
|
|
|
|
|
const base = await window.crypto.subtle.importKey(
|
|
|
|
|
"raw",
|
|
|
|
|
encoder.encode(password),
|
|
|
|
|
"PBKDF2",
|
|
|
|
|
false,
|
|
|
|
|
["deriveBits", "deriveKey"]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const authSalt = new Uint8Array([...salt, ...encoder.encode("auth")]);
|
|
|
|
|
const authKeyBits = await window.crypto.subtle.deriveBits(
|
|
|
|
|
{
|
|
|
|
|
name: "PBKDF2",
|
|
|
|
|
salt: authSalt,
|
|
|
|
|
iterations: 100000,
|
|
|
|
|
hash: "SHA-256"
|
|
|
|
|
},
|
|
|
|
|
base,
|
|
|
|
|
256
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const encryptionSalt = new Uint8Array([...salt, ...encoder.encode("enc")]);
|
|
|
|
|
const encryptionKey = await window.crypto.subtle.deriveKey(
|
|
|
|
|
{
|
|
|
|
|
name: "PBKDF2",
|
|
|
|
|
salt: encryptionSalt,
|
|
|
|
|
iterations: 100000,
|
|
|
|
|
hash: "SHA-256"
|
|
|
|
|
},
|
|
|
|
|
base,
|
|
|
|
|
{ name: "AES-GCM", length: 256 },
|
|
|
|
|
false,
|
|
|
|
|
["encrypt", "decrypt"]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
console.log(convertBufferToHex(authKeyBits));
|
|
|
|
|
console.log(encryptionKey);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
authenticationKeyString: convertBufferToHex(authKeyBits),
|
|
|
|
|
encryptionKeyObject: encryptionKey
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function openKeyDB() {
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
const request = indexedDB.open("PasswordManagerVault", 1);
|
|
|
|
|
|
|
|
|
|
request.onupgradeneeded = (event) => {
|
|
|
|
|
const db = event.target.result;
|
|
|
|
|
if (!db.objectStoreNames.contains("cryptoKeys")) {
|
|
|
|
|
db.createObjectStore("cryptoKeys");
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
request.onsuccess = () => resolve(request.result);
|
|
|
|
|
request.onerror = () => reject(request.error);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function saveEncryptionKey(cryptoKeyObject) {
|
|
|
|
|
const db = await openKeyDB();
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
const transaction = db.transaction(["cryptoKeys"], "readwrite");
|
|
|
|
|
const store = transaction.objectStore("cryptoKeys");
|
|
|
|
|
|
|
|
|
|
// We store the object under the label "masterEncryptionKey"
|
|
|
|
|
const request = store.put(cryptoKeyObject, "masterEncryptionKey");
|
|
|
|
|
|
|
|
|
|
request.onsuccess = () => resolve(true);
|
|
|
|
|
request.onerror = () => reject(request.error);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function convertBufferToHex(buffer) {
|
|
|
|
|
const bytes = new Uint8Array(buffer);
|
|
|
|
|
|
|
|
|
|
return Array.from(bytes)
|
|
|
|
|
.map(byte => byte.toString(16).padStart(2, "0"))
|
|
|
|
|
.join("");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function hexToUint8Array(hexString) {
|
|
|
|
|
if (hexString.length % 2 !== 0) {
|
|
|
|
|
throw new Error("Invalid hex string");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const array = new Uint8Array(hexString.length / 2);
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < hexString.length; i += 2) {
|
|
|
|
|
array[i / 2] = parseInt(hexString.substring(i, i + 2), 16);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return array;
|
|
|
|
|
}
|
|
|
|
|
|