diff --git a/src/utils/encryption.ts b/src/utils/encryption.ts index 16751a7..680d2f5 100644 --- a/src/utils/encryption.ts +++ b/src/utils/encryption.ts @@ -10,26 +10,46 @@ export function encrypt(plaintext: string): string { const iv = crypto.randomBytes(IV_LENGTH); const cipher = crypto.createCipheriv(ALGORITHM, key, iv); - let encrypted = cipher.update(plaintext, 'utf8', 'hex'); - encrypted += cipher.final('hex'); + const encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]); const tag = cipher.getAuthTag(); - return iv.toString('hex') + ':' + encrypted + ':' + tag.toString('hex'); + // Use PHP-compatible format: base64(nonce + ciphertext + tag) + return Buffer.concat([iv, encrypted, tag]).toString('base64'); } export function decrypt(ciphertext: string): string { const key = Buffer.from(config.totp.encryptionKey, 'hex'); - const parts = ciphertext.split(':'); - if (parts.length !== 3) throw new Error('Invalid ciphertext format'); - const iv = Buffer.from(parts[0], 'hex'); - const encrypted = parts[1]; - const tag = Buffer.from(parts[2], 'hex'); + // Detect format: PHP uses base64(nonce+ciphertext+tag), TS uses hex:hex:hex + const parts = ciphertext.split(':'); + if (parts.length === 3) { + // TS format: iv:encrypted:tag (hex) + const iv = Buffer.from(parts[0], 'hex'); + const encrypted = parts[1]; + const tag = Buffer.from(parts[2], 'hex'); + + const decipher = crypto.createDecipheriv(ALGORITHM, key, iv); + decipher.setAuthTag(tag); + + let decrypted = decipher.update(encrypted, 'hex', 'utf8'); + decrypted += decipher.final('utf8'); + return decrypted; + } + + // PHP format: base64(nonce + ciphertext + tag) + const raw = Buffer.from(ciphertext, 'base64'); + if (raw.length < IV_LENGTH + TAG_LENGTH + 1) { + throw new Error('Invalid ciphertext format'); + } + + const iv = raw.subarray(0, IV_LENGTH); + const tag = raw.subarray(raw.length - TAG_LENGTH); + const encrypted = raw.subarray(IV_LENGTH, raw.length - TAG_LENGTH); const decipher = crypto.createDecipheriv(ALGORITHM, key, iv); decipher.setAuthTag(tag); - let decrypted = decipher.update(encrypted, 'hex', 'utf8'); - decrypted += decipher.final('utf8'); - return decrypted; + let decrypted = decipher.update(encrypted); + const final = decipher.final(); + return Buffer.concat([decrypted, final]).toString('utf8'); }