(encryptedValue: string, key: Buffer)
| 34 | * or when the GCM auth tag does not verify (tampered ciphertext, wrong key). |
| 35 | */ |
| 36 | export async function decrypt(encryptedValue: string, key: Buffer): Promise<{ decrypted: string }> { |
| 37 | assertKey(key) |
| 38 | |
| 39 | const parts = encryptedValue.split(':') |
| 40 | if (parts.length < 3) { |
| 41 | throw new Error('Invalid encrypted value format. Expected "iv:encrypted:authTag"') |
| 42 | } |
| 43 | |
| 44 | const ivHex = parts[0] |
| 45 | const authTagHex = parts[parts.length - 1] |
| 46 | const encrypted = parts.slice(1, -1).join(':') |
| 47 | |
| 48 | if (!ivHex || !authTagHex) { |
| 49 | throw new Error('Invalid encrypted value format. Expected "iv:encrypted:authTag"') |
| 50 | } |
| 51 | |
| 52 | const iv = Buffer.from(ivHex, 'hex') |
| 53 | const authTag = Buffer.from(authTagHex, 'hex') |
| 54 | |
| 55 | const decipher = createDecipheriv('aes-256-gcm', key, iv, { authTagLength: 16 }) |
| 56 | decipher.setAuthTag(authTag) |
| 57 | |
| 58 | let decrypted = decipher.update(encrypted, 'hex', 'utf8') |
| 59 | decrypted += decipher.final('utf8') |
| 60 | |
| 61 | return { decrypted } |
| 62 | } |
| 63 | |
| 64 | function assertKey(key: Buffer): void { |
| 65 | if (key.length !== 32) { |
no test coverage detected