Modified by www.SourceFiles.app
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