Surface audit generations on dashboard audits
This commit is contained in:
52
v2_elemente/crypto.ts
Normal file
52
v2_elemente/crypto.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* convex/lib/crypto.ts
|
||||
*
|
||||
* Symmetrische Ver-/Entschlüsselung der hinterlegten Kunden-Secrets
|
||||
* (BYO-API-Keys, SMTP-Passwörter). Nutzt Web Crypto (AES-GCM).
|
||||
*
|
||||
* WICHTIG: Nur aus Convex *Actions* aufrufen — encrypt() braucht
|
||||
* Zufalls-IV (nicht-deterministisch) und ist daher in Mutations/Queries
|
||||
* nicht erlaubt. Der Master-Key kommt aus der Umgebung (niemals ins Repo).
|
||||
*
|
||||
* Env: SECRET_ENCRYPTION_KEY = base64-kodierter 32-Byte-Schlüssel.
|
||||
*/
|
||||
|
||||
function getKeyMaterial(): Promise<CryptoKey> {
|
||||
const b64 = process.env.SECRET_ENCRYPTION_KEY;
|
||||
if (!b64) throw new Error("SECRET_ENCRYPTION_KEY ist nicht gesetzt");
|
||||
const raw = Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
|
||||
return crypto.subtle.importKey("raw", raw, "AES-GCM", false, [
|
||||
"encrypt",
|
||||
"decrypt",
|
||||
]);
|
||||
}
|
||||
|
||||
/** Klartext → "ivBase64:cipherBase64". */
|
||||
export async function encryptSecret(plaintext: string): Promise<string> {
|
||||
const key = await getKeyMaterial();
|
||||
const iv = crypto.getRandomValues(new Uint8Array(12));
|
||||
const enc = new TextEncoder().encode(plaintext);
|
||||
const cipher = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, key, enc);
|
||||
return `${toB64(iv)}:${toB64(new Uint8Array(cipher))}`;
|
||||
}
|
||||
|
||||
/** "ivBase64:cipherBase64" → Klartext. */
|
||||
export async function decryptSecret(stored: string): Promise<string> {
|
||||
const key = await getKeyMaterial();
|
||||
const [ivB64, cipherB64] = stored.split(":");
|
||||
const iv = fromB64(ivB64);
|
||||
const cipher = fromB64(cipherB64);
|
||||
const plain = await crypto.subtle.decrypt(
|
||||
{ name: "AES-GCM", iv },
|
||||
key,
|
||||
cipher,
|
||||
);
|
||||
return new TextDecoder().decode(plain);
|
||||
}
|
||||
|
||||
function toB64(bytes: Uint8Array): string {
|
||||
return btoa(String.fromCharCode(...bytes));
|
||||
}
|
||||
function fromB64(b64: string): Uint8Array {
|
||||
return Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
|
||||
}
|
||||
Reference in New Issue
Block a user