More api fixes

This commit is contained in:
Tyler
2025-05-17 14:49:12 -10:00
committed by GitHub
parent b7a85b8d84
commit 973aa0f20f
4 changed files with 150 additions and 60 deletions
+19 -4
View File
@@ -10,6 +10,23 @@ const IV_LENGTH = 12;
const PBKDF2_ITERATIONS = 200_000;
const KEY_LENGTH = 256;
// ===== Binary-safe Base64 Helpers =====
function base64Encode(buffer) {
const binary = Array.from(new Uint8Array(buffer))
.map(byte => String.fromCharCode(byte))
.join('');
return btoa(binary);
}
function base64Decode(b64str) {
const binary = atob(b64str);
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) {
bytes[i] = binary.charCodeAt(i);
}
return bytes;
}
// ===== Key Derivation =====
/**
* Derives an AES-GCM key from a password using PBKDF2.
@@ -66,7 +83,7 @@ export async function encryptAdvanced(message, password) {
output.set(iv, salt.length);
output.set(new Uint8Array(ciphertext), salt.length + iv.length);
return btoa(String.fromCharCode(...output));
return base64Encode(output.buffer);
}
// ===== Decryption =====
@@ -77,9 +94,7 @@ export async function encryptAdvanced(message, password) {
* @returns {Promise<string>} - Decrypted plaintext.
*/
export async function decryptAdvanced(encryptedData, password) {
const encrypted = new Uint8Array(
atob(encryptedData).split('').map(c => c.charCodeAt(0))
);
const encrypted = base64Decode(encryptedData);
const salt = encrypted.slice(0, SALT_LENGTH);
const iv = encrypted.slice(SALT_LENGTH, SALT_LENGTH + IV_LENGTH);
+54 -11
View File
@@ -1,19 +1,43 @@
import { deriveKey } from "./encryption.js"; // assuming shared deriveKey()
const SALT_LENGTH = 16;
const IV_LENGTH = 12;
const KEY_LENGTH = 256;
/**
* File operations module.
* Handles file encryption and decryption operations.
* Encrypts a full file and downloads the encrypted version.
*/
// ===== Constants =====
const CHUNK_SIZE = 1024 * 1024; // 1MB chunks
// ===== Public Interface =====
export async function encryptFile(fileInput, password) {
const file = fileInput.files[0];
if (!file) return;
try {
const encryptedChunks = await processFile(file, password, true);
downloadEncryptedFile(encryptedChunks, file.name);
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);
}
@@ -24,8 +48,27 @@ export async function decryptFile(fileInput, password) {
if (!file) return;
try {
const decryptedChunks = await processFile(file, password, false);
downloadDecryptedFile(decryptedChunks, file.name);
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);
}