Reference source · 0.1.0-dev.11
opaque.ts
src/opaque.ts · Download original file
import { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto';
import { b64, unb64, jsonBytes, parseObject, requireThat, ProtocolError } from './core.ts';
// Host-internal AEAD for opaque cursors and encrypted recovery receipts, not a wire crypto suite.
export function protect(body: Record<string, any>, key: Buffer, context: Buffer): string {
requireThat(key.length === 32);
const nonce = randomBytes(12), clear = jsonBytes(body);
try {
const cipher = createCipheriv('aes-256-gcm', key, nonce); cipher.setAAD(context);
const ciphertext = Buffer.concat([cipher.update(clear), cipher.final()]);
return b64(Buffer.concat([nonce, cipher.getAuthTag(), ciphertext]));
} finally { clear.fill(0); }
}
export function reveal(value: string, key: Buffer, context: Buffer): Record<string, any> {
requireThat(typeof value === 'string' && value.length <= 12000 && key.length === 32, 'invalid_opaque_value');
const bytes = unb64(value); requireThat(bytes.length >= 30, 'invalid_opaque_value');
let clear: Buffer | undefined;
try {
const decipher = createDecipheriv('aes-256-gcm', key, bytes.subarray(0, 12));
decipher.setAAD(context); decipher.setAuthTag(bytes.subarray(12, 28));
clear = Buffer.concat([decipher.update(bytes.subarray(28)), decipher.final()]);
return parseObject(clear);
} catch { throw new ProtocolError('invalid_opaque_value'); }
finally { clear?.fill(0); }
}