/* ===================================================================== crypto.js — Verschlüsselung & Hashing für das Zugangssystem. 1:1 portiert aus cloudflare-worker/src/lib/crypto.js (Web Crypto API, in Node 20+ global verfügbar wie im Cloudflare Worker — keine Änderung nötig). ===================================================================== */ const CODE_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"; // ohne I,O,0,1 export function generateFriendlyCode() { const bytes = crypto.getRandomValues(new Uint8Array(12)); let raw = ""; for (const b of bytes) raw += CODE_ALPHABET[b % CODE_ALPHABET.length]; return `${raw.slice(0, 4)}-${raw.slice(4, 8)}-${raw.slice(8, 12)}`; } export function generateSessionToken() { const bytes = crypto.getRandomValues(new Uint8Array(32)); return [...bytes].map((b) => b.toString(16).padStart(2, "0")).join(""); } export function generateId() { return crypto.randomUUID(); } export async function sha256Hex(text) { const buf = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(text)); return [...new Uint8Array(buf)].map((b) => b.toString(16).padStart(2, "0")).join(""); } async function getAesKey() { const raw = Uint8Array.from(atob(process.env.ENCRYPTION_KEY), (c) => c.charCodeAt(0)); return crypto.subtle.importKey("raw", raw, "AES-GCM", false, ["encrypt", "decrypt"]); } function bufToBase64(buf) { return btoa(String.fromCharCode(...new Uint8Array(buf))); } function base64ToBuf(b64) { return Uint8Array.from(atob(b64), (c) => c.charCodeAt(0)); } /** Verschlüsselt einen Klartext-Code reversibel. Rückgabe: "iv.ciphertext" (base64). */ export async function encryptCode(plaintext) { const key = await getAesKey(); const iv = crypto.getRandomValues(new Uint8Array(12)); const ciphertext = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, key, new TextEncoder().encode(plaintext)); return `${bufToBase64(iv)}.${bufToBase64(ciphertext)}`; } /** Entschlüsselt einen mit encryptCode() erzeugten Wert. */ export async function decryptCode(encoded) { const [ivB64, ctB64] = encoded.split("."); const key = await getAesKey(); const iv = base64ToBuf(ivB64); const ciphertext = base64ToBuf(ctB64); const plainBuf = await crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, ciphertext); return new TextDecoder().decode(plainBuf); } export function nowIso() { return new Date().toISOString(); }