import { postData } from "./client.js"; const loginForm = document.getElementById("loginForm"); const error = document.querySelector(".error"); const errorMessage = document.querySelector(".error p"); const success = document.querySelector(".success"); openKeyDB(); if (loginForm) loginForm.onsubmit = async e => { e.preventDefault(); 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); let resultObject = await postData("/login", { username: document.getElementById("logUser").value, password: authenticationKeyString }); if (resultObject.message.includes("success")) { error.style.display = "none"; success.style.display = "flex"; } else { errorMessage.innerHTML = resultObject.message; error.style.display = "flex"; } if (resultObject.success) { window.location.href = "/home" localStorage.setItem("token", resultObject.token); } }; 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; }