import { aes128CbcDecrypt } from './aes.ts' const encoder = new TextEncoder() /** * Decrypt an AAXC license voucher (key/iv) bound to this device and customer. * Same algorithm as mkb79/Audible `decrypt_voucher_from_licenserequest`. */ export async function decryptVoucher(input: { deviceType: string serial: string customerId: string asin: string encryptedVoucher: string }): Promise<{ key?: string; iv?: string; rules?: unknown }> { const digest = new Uint8Array( await crypto.subtle.digest( 'SHA-256', encoder.encode(`${input.deviceType}${input.serial}${input.customerId}${input.asin}`), ), ) const binary = atob(input.encryptedVoucher) const cipherBytes = new Uint8Array(binary.length) for (let i = 0; i < binary.length; i++) cipherBytes[i] = binary.charCodeAt(i) const plaintext = new TextDecoder().decode( aes128CbcDecrypt(digest.subarray(0, 16), digest.subarray(16), cipherBytes), ) try { const parsed = JSON.parse(plaintext) as { key?: string; iv?: string; rules?: unknown } return { key: parsed.key, iv: parsed.iv, rules: parsed.rules } } catch { const match = plaintext.match( /^\{\s*"key"\s*:\s*"(?[^"]*)"\s*,\s*"iv"\s*:\s*"(?[^"]*)"/, ) if (match?.groups) return { key: match.groups.key, iv: match.groups.iv } throw new Error('Failed to parse decrypted Audible voucher JSON.') } }