51 lines
2.0 KiB
TypeScript
51 lines
2.0 KiB
TypeScript
import { createCipheriv, createDecipheriv, createHash, randomBytes, timingSafeEqual } from 'node:crypto';
|
|
import type { AppConfig } from './config.js';
|
|
|
|
export function randomToken(bytes = 32): string {
|
|
return randomBytes(bytes).toString('base64url');
|
|
}
|
|
|
|
export function hashToken(value: string): Buffer {
|
|
return createHash('sha256').update(value).digest();
|
|
}
|
|
|
|
export function sameTokenHash(left: Buffer, right: Buffer): boolean {
|
|
return left.length === right.length && timingSafeEqual(left, right);
|
|
}
|
|
|
|
export function encryptText(config: AppConfig, value: string): string {
|
|
return encryptBytes(config, Buffer.from(value, 'utf8'));
|
|
}
|
|
|
|
export function encryptBytes(config: AppConfig, value: Buffer): string {
|
|
const iv = randomBytes(12);
|
|
const cipher = createCipheriv('aes-256-gcm', config.fieldEncryptionKey, iv);
|
|
const ciphertext = Buffer.concat([cipher.update(value), cipher.final()]);
|
|
const tag = cipher.getAuthTag();
|
|
return ['v1', iv.toString('base64url'), tag.toString('base64url'), ciphertext.toString('base64url')].join('.');
|
|
}
|
|
|
|
export function decryptText(config: AppConfig, value: string | null | undefined): string {
|
|
return decryptBytes(config, value).toString('utf8');
|
|
}
|
|
|
|
export function decryptBytes(config: AppConfig, value: string | null | undefined): Buffer {
|
|
if (!value) return Buffer.alloc(0);
|
|
const [version, ivText, tagText, ciphertextText] = value.split('.');
|
|
if (version !== 'v1' || !ivText || !tagText || !ciphertextText) throw new Error('Invalid encrypted field format.');
|
|
const decipher = createDecipheriv('aes-256-gcm', config.fieldEncryptionKey, Buffer.from(ivText, 'base64url'));
|
|
decipher.setAuthTag(Buffer.from(tagText, 'base64url'));
|
|
return Buffer.concat([
|
|
decipher.update(Buffer.from(ciphertextText, 'base64url')),
|
|
decipher.final()
|
|
]);
|
|
}
|
|
|
|
export function sha256Text(value: string): string {
|
|
return createHash('sha256').update(value).digest('hex');
|
|
}
|
|
|
|
export function sha256Bytes(value: Buffer): string {
|
|
return createHash('sha256').update(value).digest('hex');
|
|
}
|