import { deriveKey } from "./encryption.js"; // assuming shared deriveKey() const SALT_LENGTH = 16; const IV_LENGTH = 12; const KEY_LENGTH = 256; /** * Encrypts a full file and downloads the encrypted version. */ export async function encryptFile(fileInput, password) { const file = fileInput.files[0]; if (!file) return; try { const salt = crypto.getRandomValues(new Uint8Array(SALT_LENGTH)); const iv = crypto.getRandomValues(new Uint8Array(IV_LENGTH)); const key = await deriveKey(password, salt); const fileBuffer = new Uint8Array(await file.arrayBuffer()); const ciphertext = await crypto.subtle.encrypt( { name: "AES-GCM", iv }, key, fileBuffer ); const ctBytes = new Uint8Array(ciphertext); const result = new Uint8Array(salt.length + iv.length + ctBytes.length); result.set(salt); result.set(iv, salt.length); result.set(ctBytes, salt.length + iv.length); const blob = new Blob([result], { type: "application/octet-stream" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = file.name + ".encrypted"; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } catch (error) { alert("Error encrypting file: " + error.message); } } export async function decryptFile(fileInput, password) { const file = fileInput.files[0]; if (!file) return; try { const data = new Uint8Array(await file.arrayBuffer()); const salt = data.slice(0, SALT_LENGTH); const iv = data.slice(SALT_LENGTH, SALT_LENGTH + IV_LENGTH); const ciphertext = data.slice(SALT_LENGTH + IV_LENGTH); const key = await deriveKey(password, salt); const decrypted = await crypto.subtle.decrypt( { name: "AES-GCM", iv }, key, ciphertext ); const blob = new Blob([decrypted], { type: "application/octet-stream" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = file.name.replace(".encrypted", ""); document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } catch (error) { alert("Error decrypting file: " + error.message); } } // ===== File Processing ===== async function processFile(file, password, isEncrypt) { const chunks = []; const totalChunks = Math.ceil(file.size / CHUNK_SIZE); let processedChunks = 0; for (let start = 0; start < file.size; start += CHUNK_SIZE) { const chunk = file.slice(start, start + CHUNK_SIZE); const arrayBuffer = await chunk.arrayBuffer(); const uint8Array = new Uint8Array(arrayBuffer); const processedChunk = await processChunk(uint8Array, password, isEncrypt); chunks.push(processedChunk); processedChunks++; updateProgress(processedChunks, totalChunks); } return chunks; } async function processChunk(data, password, isEncrypt) { const payload = { "encryption-type": "advanced", operation: isEncrypt ? "encrypt" : "decrypt", message: Array.from(data).join(','), password: password }; const response = await fetch("/", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload) }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const result = await response.json(); return new Uint8Array(result.result.split(',').map(Number)); } // ===== File Download ===== function downloadEncryptedFile(chunks, originalName) { const blob = new Blob(chunks, { type: 'application/octet-stream' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = originalName + '.encrypted'; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } function downloadDecryptedFile(chunks, originalName) { const blob = new Blob(chunks, { type: 'application/octet-stream' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = originalName.replace('.encrypted', ''); document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } // ===== Progress Tracking ===== function updateProgress(processed, total) { const progressBar = document.getElementById("file-progress"); const progressText = document.getElementById("file-progress-text"); if (progressBar && progressText) { const percent = Math.round((processed / total) * 100); progressBar.style.width = percent + "%"; progressText.textContent = `Processing: ${percent}%`; if (processed === total) { setTimeout(() => { progressBar.style.width = "0%"; progressText.textContent = ""; }, 1000); } } }