This repository has been archived on 2025-08-23. You can view files and clone it, but cannot push or open issues or pull requests.
Files
GreenCoast/client/crypto.js
2025-08-21 20:56:38 -04:00

37 lines
1.7 KiB
JavaScript

export async function deriveKey(passphrase, saltBytes) {
const enc = new TextEncoder();
const keyMaterial = await crypto.subtle.importKey("raw", enc.encode(passphrase), { name: "PBKDF2" }, false, ["deriveKey"]);
return crypto.subtle.deriveKey(
{ name: "PBKDF2", salt: saltBytes, iterations: 120_000, hash: "SHA-256" },
keyMaterial,
{ name: "AES-GCM", length: 256 },
false,
["encrypt", "decrypt"]
);
}
export async function encryptString(plaintext, passphrase) {
const enc = new TextEncoder();
const salt = crypto.getRandomValues(new Uint8Array(16));
const iv = crypto.getRandomValues(new Uint8Array(12));
const key = await deriveKey(passphrase, salt);
const ct = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, key, enc.encode(plaintext));
const version = new Uint8Array([1]);
const out = new Uint8Array(1 + 16 + 12 + ct.byteLength);
out.set(version, 0); out.set(salt, 1); out.set(iv, 17); out.set(new Uint8Array(ct), 29);
return out;
}
export async function decryptToString(payload, passphrase) {
const dec = new TextDecoder();
if (!(payload instanceof Uint8Array)) payload = new Uint8Array(payload);
if (payload.length < 29) throw new Error("ciphertext too short");
if (payload[0] !== 1) throw new Error("unknown version");
const salt = payload.slice(1, 17), iv = payload.slice(17, 29), ct = payload.slice(29);
const key = await deriveKey(passphrase, salt);
const pt = await crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, ct);
return dec.decode(pt);
}
export function toBlob(data) {
if (data instanceof Uint8Array) return new Blob([data], { type: "application/octet-stream" });
return new Blob([data], { type: "application/json;charset=utf-8" });
}