feat: phase 4 completed

This commit is contained in:
Yusuf İpek
2025-12-27 14:28:33 +03:00
parent 70d246bc17
commit 226935e67d
12 changed files with 510 additions and 31 deletions
+96
View File
@@ -0,0 +1,96 @@
export async function deriveKeyFromPassword(password: string): Promise<string> {
const encoder = new TextEncoder();
const keyMaterial = await crypto.subtle.importKey(
"raw",
encoder.encode(password),
"PBKDF2",
false,
["deriveBits", "deriveKey"]
);
const key = await crypto.subtle.deriveKey(
{
name: "PBKDF2",
salt: encoder.encode("dead-man-switch-salt"),
iterations: 100000,
hash: "SHA-256",
},
keyMaterial,
{ name: "AES-GCM", length: 256 },
true,
["encrypt", "decrypt"]
);
const exported = await crypto.subtle.exportKey("raw", key);
return btoa(String.fromCharCode(...new Uint8Array(exported)));
}
export async function generateKey(): Promise<string> {
const key = await crypto.subtle.generateKey(
{ name: "AES-GCM", length: 256 },
true,
["encrypt", "decrypt"]
);
const exported = await crypto.subtle.exportKey("raw", key);
return btoa(String.fromCharCode(...new Uint8Array(exported)));
}
export async function importKey(keyString: string): Promise<CryptoKey> {
const keyData = Uint8Array.from(atob(keyString), (c) => c.charCodeAt(0));
return crypto.subtle.importKey(
"raw",
keyData,
{ name: "AES-GCM", length: 256 },
false,
["encrypt", "decrypt"]
);
}
export async function encrypt(data: string, keyString: string): Promise<string> {
const key = await importKey(keyString);
const iv = crypto.getRandomValues(new Uint8Array(12));
const encoded = new TextEncoder().encode(data);
const encrypted = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv },
key,
encoded
);
const combined = new Uint8Array(iv.length + encrypted.byteLength);
combined.set(iv);
combined.set(new Uint8Array(encrypted), iv.length);
return btoa(String.fromCharCode(...combined));
}
export async function decrypt(encryptedData: string, keyString: string): Promise<string> {
const key = await importKey(keyString);
const combined = Uint8Array.from(atob(encryptedData), (c) => c.charCodeAt(0));
const iv = combined.slice(0, 12);
const data = combined.slice(12);
const decrypted = await crypto.subtle.decrypt(
{ name: "AES-GCM", iv },
key,
data
);
return new TextDecoder().decode(decrypted);
}
export async function encryptFile(file: File, keyString: string): Promise<Blob> {
const key = await importKey(keyString);
const iv = crypto.getRandomValues(new Uint8Array(12));
const arrayBuffer = await file.arrayBuffer();
const encrypted = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv },
key,
arrayBuffer
);
const combined = new Uint8Array(iv.length + encrypted.byteLength);
combined.set(iv);
combined.set(new Uint8Array(encrypted), iv.length);
return new Blob([combined], { type: "application/octet-stream" });
}
export async function decryptFile(encryptedBlob: Blob, keyString: string): Promise<ArrayBuffer> {
const key = await importKey(keyString);
const combined = new Uint8Array(await encryptedBlob.arrayBuffer());
const iv = combined.slice(0, 12);
const data = combined.slice(12);
return crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, data);
}