← Public packages
@danj/audible-auth
Locked Audible credential gateway: Connect, token refresh, and narrow Audible calls for other Kody packages.
src/voucher.ts
38 lines · 1.3 KB · TypeScriptimport { 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*"(?<key>[^"]*)"\s*,\s*"iv"\s*:\s*"(?<iv>[^"]*)"/,
)
if (match?.groups) return { key: match.groups.key, iv: match.groups.iv }
throw new Error('Failed to parse decrypted Audible voucher JSON.')
}
}