import { Injectable, Logger } from "@nestjs/common"; import { ConfigService } from "@nestjs/config"; import * as jose from "jose"; @Injectable() export class EncryptionService { private readonly logger = new Logger(EncryptionService.name); private readonly jwtSecret: Uint8Array; private readonly encryptionKey: Uint8Array; constructor(private configService: ConfigService) { const secret = this.configService.get("JWT_SECRET"); this.jwtSecret = new TextEncoder().encode( secret || "default-secret-change-me-in-production", ); const encKey = this.configService.get("ENCRYPTION_KEY"); if (!encKey) { this.logger.warn( "ENCRYPTION_KEY is not defined, using a default insecure key for development", ); } const rawKey = encKey || "default-encryption-key-32-chars-"; this.encryptionKey = new TextEncoder().encode( rawKey.padEnd(32, "0").substring(0, 32), ); } async encryptContent(content: string): Promise { const data = new TextEncoder().encode(content); return new jose.CompactEncrypt(data) .setProtectedHeader({ alg: "dir", enc: "A256GCM" }) .encrypt(this.encryptionKey); } async decryptContent(jwe: string): Promise { const { plaintext } = await jose.compactDecrypt(jwe, this.encryptionKey); return new TextDecoder().decode(plaintext); } async signContent(content: string): Promise { const data = new TextEncoder().encode(content); return new jose.CompactSign(data) .setProtectedHeader({ alg: "HS256" }) .sign(this.jwtSecret); } async verifyContentSignature(jws: string): Promise { const { payload } = await jose.compactVerify(jws, this.jwtSecret); return new TextDecoder().decode(payload); } getPgpEncryptionKey(): string { return ( this.configService.get("PGP_ENCRYPTION_KEY") || "default-pgp-key" ); } }