Terminal
This commit is contained in:
@@ -1,55 +0,0 @@
|
||||
// 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