Modified by www.SourceFiles.app
This commit is contained in:
@@ -0,0 +1,55 @@
|
|||||||
|
// Sealing for mailbox passwords. Server-only: never import from a route or a
|
||||||
|
// *.functions.ts top level, both of which ship to the client bundle.
|
||||||
|
//
|
||||||
|
// AES-256-GCM so the stored value is both encrypted and tamper-evident — a
|
||||||
|
// modified ciphertext fails the auth tag rather than decrypting to garbage that
|
||||||
|
// then gets sent to an IMAP server.
|
||||||
|
import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
|
||||||
|
|
||||||
|
export type Sealed = { ciphertext: string; iv: string; tag: string };
|
||||||
|
|
||||||
|
const KEY_ENV = "MAIL_CRED_KEY";
|
||||||
|
|
||||||
|
// 32 bytes, supplied base64 or hex. Generate with:
|
||||||
|
// openssl rand -base64 32
|
||||||
|
function key(): Buffer {
|
||||||
|
const raw = process.env[KEY_ENV];
|
||||||
|
if (!raw) {
|
||||||
|
throw new Error(
|
||||||
|
`${KEY_ENV} is not set. Add a 32-byte key to .env.secret (openssl rand -base64 32) before using mail.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const buf = /^[0-9a-fA-F]{64}$/.test(raw.trim())
|
||||||
|
? Buffer.from(raw.trim(), "hex")
|
||||||
|
: Buffer.from(raw.trim(), "base64");
|
||||||
|
if (buf.length !== 32) {
|
||||||
|
throw new Error(`${KEY_ENV} must decode to exactly 32 bytes (got ${buf.length}).`);
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function seal(plaintext: string): Sealed {
|
||||||
|
const iv = randomBytes(12); // 96-bit nonce, the GCM standard
|
||||||
|
const c = createCipheriv("aes-256-gcm", key(), iv);
|
||||||
|
const ct = Buffer.concat([c.update(plaintext, "utf8"), c.final()]);
|
||||||
|
return {
|
||||||
|
ciphertext: ct.toString("base64"),
|
||||||
|
iv: iv.toString("base64"),
|
||||||
|
tag: c.getAuthTag().toString("base64"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function open(s: Sealed): string {
|
||||||
|
const d = createDecipheriv("aes-256-gcm", key(), Buffer.from(s.iv, "base64"));
|
||||||
|
d.setAuthTag(Buffer.from(s.tag, "base64"));
|
||||||
|
return Buffer.concat([d.update(Buffer.from(s.ciphertext, "base64")), d.final()]).toString("utf8");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mailKeyConfigured(): boolean {
|
||||||
|
try {
|
||||||
|
key();
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user