// Sealing a token to one recipient (novox/hq ADR 0183): the manager seals what it hands a node to that // node's agent module key, and a node seals a waiting login to the key the manager names. X25519 for // the agreement, HKDF-SHA256 for the key, AES-256-GCM for the box — all from Node's own library, so a // bundle carries no dependency and no secret ever crosses the bus in the clear. // // A sealed box is `{ v: 1, eph, iv, tag, ct }`, every field base64. `eph` is a one-time public key, so // two boxes of one value to one recipient share nothing, and only the recipient's private key opens it. import { createCipheriv, createDecipheriv, createPrivateKey, createPublicKey, diffieHellman, generateKeyPairSync, hkdfSync, randomBytes, type KeyObject, } from "node:crypto"; export interface SealedBox { readonly v: 1; readonly eph: string; readonly iv: string; readonly tag: string; readonly ct: string; } /** A recipient's keypair, as the two PEM strings it is kept and published as. */ export interface KeyPairPem { readonly publicKey: string; readonly privateKey: string; } const INFO = Buffer.from("novox-mesh sealed box v1"); export function generateKeyPair(): KeyPairPem { const { publicKey, privateKey } = generateKeyPairSync("x25519"); return { publicKey: publicKey.export({ type: "spki", format: "pem" }).toString(), privateKey: privateKey.export({ type: "pkcs8", format: "pem" }).toString(), }; } function keyFor(secret: Buffer, eph: Buffer, recipient: Buffer): Buffer { // The ephemeral and the recipient's public halves are bound into the key, so a box cannot be // re-addressed to another recipient by swapping its `eph`. return Buffer.from(hkdfSync("sha256", secret, Buffer.concat([eph, recipient]), INFO, 32)); } function rawPublic(key: KeyObject): Buffer { return key.export({ type: "spki", format: "der" }).subarray(-32); } export function seal(plaintext: string, recipientPublicPem: string): SealedBox { const recipient = createPublicKey(recipientPublicPem); const eph = generateKeyPairSync("x25519"); const secret = diffieHellman({ privateKey: eph.privateKey, publicKey: recipient }); const ephRaw = eph.publicKey.export({ type: "spki", format: "der" }); const key = keyFor(secret, ephRaw, rawPublic(recipient)); const iv = randomBytes(12); const cipher = createCipheriv("aes-256-gcm", key, iv); const ct = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]); return { v: 1, eph: ephRaw.toString("base64"), iv: iv.toString("base64"), tag: cipher.getAuthTag().toString("base64"), ct: ct.toString("base64"), }; } /** Open a box with the recipient's private key. Throws on a box for another key or one tampered with. */ export function open(box: SealedBox, privateKeyPem: string): string { if (!box || box.v !== 1) throw new Error("not a sealed box this module can open"); const priv = createPrivateKey(privateKeyPem); const ephRaw = Buffer.from(box.eph, "base64"); const eph = createPublicKey({ key: ephRaw, format: "der", type: "spki" }); const secret = diffieHellman({ privateKey: priv, publicKey: eph }); const key = keyFor(secret, ephRaw, rawPublic(createPublicKey(priv))); const decipher = createDecipheriv("aes-256-gcm", key, Buffer.from(box.iv, "base64")); decipher.setAuthTag(Buffer.from(box.tag, "base64")); return Buffer.concat([decipher.update(Buffer.from(box.ct, "base64")), decipher.final()]).toString("utf8"); }