@accounter/server
Version:
Accounter GraphQL server
35 lines (31 loc) • 1.36 kB
text/typescript
import { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto';
const ALGORITHM = 'aes-256-gcm';
const IV_LEN = 12;
const TAG_LEN = 16;
export function encryptCredential(plaintext: string, keyHex: string): string {
const key = Buffer.from(keyHex, 'hex');
if (key.length !== 32) {
throw new Error('Invalid key length: expected 32 bytes (64 hex characters)');
}
const iv = randomBytes(IV_LEN);
const cipher = createCipheriv(ALGORITHM, key, iv);
const encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
const authTag = cipher.getAuthTag();
return Buffer.concat([iv, authTag, encrypted]).toString('base64');
}
export function decryptCredential(blob: string, keyHex: string): string {
const buf = Buffer.from(blob, 'base64');
if (buf.length < IV_LEN + TAG_LEN) {
throw new Error('Invalid encrypted blob: too short');
}
const iv = buf.subarray(0, IV_LEN);
const authTag = buf.subarray(IV_LEN, IV_LEN + TAG_LEN);
const body = buf.subarray(IV_LEN + TAG_LEN);
const key = Buffer.from(keyHex, 'hex');
if (key.length !== 32) {
throw new Error('Invalid key length: expected 32 bytes (64 hex characters)');
}
const decipher = createDecipheriv(ALGORITHM, key, iv);
decipher.setAuthTag(authTag);
return Buffer.concat([decipher.update(body), decipher.final()]).toString('utf8');
}