Files
backend/src/integrations/tbank/tbank.crypto.ts
T

50 lines
1.4 KiB
TypeScript

import { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto';
const KEY_SIZE_BYTES = 32;
const VERSION = 1;
const NONCE_SIZE = 12;
const TAG_SIZE = 16;
export class TbankCrypto {
constructor(private readonly key: Buffer) {}
static fromBase64(key: string): TbankCrypto {
const decoded = Buffer.from(key, 'base64');
if (decoded.length !== KEY_SIZE_BYTES) {
throw new Error('Invalid token encryption key');
}
return new TbankCrypto(decoded);
}
encrypt(value: string): string {
const nonce = randomBytes(NONCE_SIZE);
const cipher = createCipheriv('aes-256-gcm', this.key, nonce);
const ciphertext = Buffer.concat([
cipher.update(value, 'utf8'),
cipher.final(),
]);
return Buffer.concat([
Buffer.from([VERSION]),
nonce,
ciphertext,
cipher.getAuthTag(),
]).toString('base64');
}
decrypt(payload: string): string {
const bytes = Buffer.from(payload, 'base64');
if (bytes[0] !== VERSION || bytes.length < 1 + NONCE_SIZE + TAG_SIZE) {
throw new Error('Invalid encrypted token');
}
const nonce = bytes.subarray(1, 1 + NONCE_SIZE);
const tag = bytes.subarray(-TAG_SIZE);
const decipher = createDecipheriv('aes-256-gcm', this.key, nonce);
decipher.setAuthTag(tag);
return Buffer.concat([
decipher.update(bytes.subarray(1 + NONCE_SIZE, -TAG_SIZE)),
decipher.final(),
]).toString('utf8');
}
}