// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0 let secretKey = localStorage.getItem("PRIVATE-secretKey") let cryptoKey = localStorage.getItem("PRIVATE-cryptoKey") if (secretKey === null || cryptoKey === null) { window.location.replace("/login") document.body.innerHTML = "Redirecting..." throw new Error(); } let remote = localStorage.getItem("SETTING-homeServer") if (remote == null) { localStorage.setItem("SETTING-homeServer", "https://notes.canary.hectabit.org") remote = "https://notes.canary.hectabit.org" } let notesPlainText = "" let information let backButton let titleBox let inputTypeGlobal = 0 document.addEventListener("DOMContentLoaded", function() { information = document.getElementById("information") backButton = document.getElementById("backButton") titleBox = document.getElementById("title") }) function showInput(inputType) { inputTypeGlobal = inputType switch (inputType) { case 0: information.innerText = "Welcome to the Burgernotes Migration wizard! Before we begin migration, there are a few things you should know." titleBox.innerText = "Burgernotes Migrator" backButton.classList.add("hidden") break case 1: information.innerText = "This migration process may corrupt your data if interrupted. Please ensure you have a stable internet connection. Press continue to download a backup of your notes, just in case." titleBox.innerText = "Disclaimer" backButton.classList.remove("hidden") break case 2: information.innerText = "Now that you have a backup of your notes, you can proceed to the next step. Press continue to begin migration." titleBox.innerText = "Backup Complete" break case 3: information.innerText = "You have successfully migrated to new Burgernotes! Enjoy the more secure and feature-rich experience. Click continue to return to the app." titleBox.innerText = "Migration Complete" break } } function buttonClick() { switch (inputTypeGlobal) { case 0: showInput(1) break case 1: exportNotes().then((data) => { if (data !== undefined) { notesPlainText = data let blob = new Blob([JSON.stringify(data)], {type: "application/json"}) let url = URL.createObjectURL(blob) let a = document.createElement("a") a.href = url a.download = "backup.json" a.click() showInput(2) } }) break case 2: importNotes(notesPlainText).then((status) => { if (status === 200) { showInput(3) } else if (status === -1 || status === -2) { titleBox.innerText = "Error!" information.innerText = "An error occurred while migrating your notes. Please try again by pressing continue." console.log(status) } else { titleBox.innerText = "Critical Error!" information.innerText = "All your notes have been lost. Good thing you have a backup! Press continue to return to the app, so you can import your backup." inputTypeGlobal = 3 } }) break case 3: window.location.href = "/app" break } } function back() { showInput(inputTypeGlobal - 1) } async function getKey() { let salt = new TextEncoder().encode("I love Burgernotes!") let cryptoKeyFormatted = await window.crypto.subtle.importKey("raw", new TextEncoder().encode(cryptoKey), "PBKDF2", false, ["deriveBits", "deriveKey"]) return await window.crypto.subtle.deriveKey({ name: "PBKDF2", salt, iterations: 1, hash: "SHA-512" }, cryptoKeyFormatted, {name: "AES-GCM", length: 256}, true, ["encrypt", "decrypt"]) } function arrayBufferToBase64(buffer) { const uint8Array = new Uint8Array(buffer); return btoa(String.fromCharCode.apply(null, uint8Array)) } async function encrypt(text) { let cryptoKey = await getKey() let iv = window.crypto.getRandomValues(new Uint8Array(12)) let encrypted = await window.crypto.subtle.encrypt({ name: "AES-GCM", iv: iv }, cryptoKey, new TextEncoder().encode(text)) return btoa(JSON.stringify({ encrypted: arrayBufferToBase64(encrypted), iv: arrayBufferToBase64(iv) })) } async function importNotes(plaintextNotes) { try { for (let i in plaintextNotes) { plaintextNotes[i]["title"] = await encrypt(plaintextNotes[i]["title"]) plaintextNotes[i]["content"] = await encrypt(plaintextNotes[i]["content"]) } let purgeNotesFetch = await fetch(remote + "/api/purgenotes", { method: "POST", body: JSON.stringify({ "secretKey": secretKey, }), headers: { "Content-Type": "application/json; charset=UTF-8" } }) if (purgeNotesFetch.status !== 200) { return -2 } let importNotesFetch = await fetch(remote + "/api/importnotes", { method: "POST", body: JSON.stringify({ "secretKey": secretKey, "notes": JSON.stringify(plaintextNotes) }), headers: { "Content-Type": "application/json; charset=UTF-8" } }) return importNotesFetch.status } catch (e) { return -1 } } async function exportNotes() { try { titleBox.innerText = "Decrypting Notes..." let oldPasswordFormat = await hashwasm.sha512(prompt("Please enter your password to decrypt your notes.")) let exportNotesFetch = await fetch(remote + "/api/exportnotes", { method: "POST", body: JSON.stringify({ secretKey: secretKey }), headers: { "Content-Type": "application/json; charset=UTF-8" } }) let responseData = await exportNotesFetch.json() for (let i in responseData) { information.innerText = "Decrypting " + i + "/" + responseData.length responseData[i]["title"] = CryptoJS.AES.decrypt(responseData[i]["title"], oldPasswordFormat).toString(CryptoJS.enc.Utf8) responseData[i]["content"] = CryptoJS.AES.decrypt(responseData[i]["content"], oldPasswordFormat).toString(CryptoJS.enc.Utf8) } titleBox.innerText = "Notes decrypted!" information.innerText = "Now if anything goes wrong, you can import this backup to restore your notes in the settings panel." return responseData } catch (e) { titleBox.innerText = "Error!" information.innerText = "An error occurred while decrypting your notes. Good thing we found out before we started migration! Please try again by pressing continue." } } // @license-end