Files
DRMLibre/docs/kfx2024-decryption-effort.md
T
JMR-devandClaude Opus 4.8 8dd81a5aed Document KFX / .kinf2024 decryption blocker and follow-up plan
Investigated decrypting a real Microsoft Store Kindle book (KFX). Found the
content is a loose DRMION container with a sibling .voucher, and the account
key material lives in .kinf2024 — a key store newer than and structurally
different from the .kinf2018 that DeDRM supports (zero "/" record separators;
not a version bump). No DeDRM/community support exists for .kinf2024.

The blocker is the key, not KFX itself: without the account DSN/secret the
voucher (and thus the book) cannot be decrypted or tested. Reverse-engineering
.kinf2024 is novel work outside the "port DeDRM" premise, so per decision the
findings, evidence, and a two-part follow-up plan (KFX engine port + .kinf2024
RE) are captured in docs/kfx2024-decryption-effort.md rather than implemented.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 19:05:54 -05:00

9.3 KiB
Raw Blame History

KFX + .kinf2024 decryption — follow-up research notes

Status: parked (not implemented). This documents what we found while trying to test Phase 4 (KFX) on a real Microsoft Store Kindle book, why it's blocked, and what a future effort would need to do.

Date investigated: 2026-06-24. Machine: Windows 11, Kindle installed via Microsoft Store.


TL;DR

  • The target book (The Chronicles of Narnia, ASIN B008LUYSAE) is in KFX form: a DRMION-wrapped .azw plus a separate .voucher and .res resources.
  • Decrypting KFX requires a per-book content key that is itself encrypted in a voucher, unlockable only with the Kindle account's DSN + account secret.
  • Those credentials live in .kinf2024 — a key store newer than and structurally different from the .kinf2018 that DeDRM supports. There is no published reverse-engineering of .kinf2024 (it is not in DeDRM_tools).
  • Therefore the blocker is the key, not the KFX format. Even a perfect KFX decryptor cannot be tested on this book until .kinf2024 is cracked. That is novel reverse-engineering of a deliberately-obfuscated, rotating format — outside this project's original "port the DeDRM logic" premise.

A follow-up has two independent parts: (A) a KFX decryption engine (a tractable port of DeDRM) and (B) .kinf2024 key extraction (a research problem). (A) is useless on Store-Kindle books without (B).


Evidence we gathered

The Kindle install (Microsoft Store)

  • Package: AMZNKindle.AmazonKindleReadingApp_m1sc522ngdk36
  • Content root: %LOCALAPPDATA%\Packages\AMZNKindle.AmazonKindleReadingApp_m1sc522ngdk36\LocalState\Classic\Content\
  • Key store: …\LocalState\Classic\Data\storage\.kinf2024

The book folder (…\Content\B008LUYSAE_EBOK\)

File Size Notes
B008LUYSAE_EBOK.azw 1,166,844 DRMION container (see magic below)
amzn1.drm-voucher.v1.77b9b3e1-…-db5a276a8d6c.voucher 1,199 Encrypted DRM voucher (holds the content key)
CR!….azw.res (many) 1–6 MB each KFX resource containers (images/fonts/text)
B008LUYSAE_EBOK.mbpV2 554 annotations/sidecar
B008LUYSAE_EBOK.phl 1,129 popular-highlights sidecar

First 16 bytes of B008LUYSAE_EBOK.azw:

ea 44 52 4d 49 4f 4e ee  e0 01 00 ea ee 9e 81 83   .DRMION.........
… "ProtectedData" … "amz…"

\xeaDRMION\xee is the KFX DRMION magic. Note: this is a loose DRMION file, not a .kfx-zip. (Our current Format::detect only recognizes DRMION inside a ZIP — see "DRMLibre changes needed" below.)

.kinf2024 facts

  • Size: 86,477 bytes.
  • Zero / (0x2F) bytes. .kinf2018/.kinf2011 are /-delimited record streams; the absence of any / means .kinf2024 uses a completely different serialization — this is not a renamed .kinf2018.
  • Content is char-map-style encoded (first 64 chars looked like bAY8YkY8bkd8AfdYA69v08b799A1AC0YC0009SAAY0d8CM94Ytz992zhb6Yt9J0J).
  • No legacy .kinf2018/.kinf2011 exists anywhere on this machine (no old desktop "Kindle for PC" install to fall back on).

There is also a Google-Drive-synced ~\My Drive\Documents\My Kindle Content\ with many classic *_EBOK.azw files from an older install. Those are also account-locked and would need the same Kindle key material to decrypt.


Background: how KFX decryption works (the tractable part — a port)

Reference: DeDRM_tools/DeDRM_plugin/kfxdedrm.py, ion.py, kfxtables.py.

  1. Identify the pieces. A KFX book is a DRMION content file (…\xeaDRMION\xee) plus a *.voucher (the DrmIonVoucher). Both are Amazon Ion binary.
  2. Parse the voucher (Ion) to read the encryption algorithm (AES), transform (CBC), hash (SHA-256) and the lock parameters: CLIENT_ID = DSN, ACCOUNT_SECRET = account token.
  3. Derive the voucher key. Build shared = "PIDv3" + algos + lockparams, run it through 13 obfuscation variants (ion.py obfuscate* / process_V*, ~lines 1051–1610), then key = HMAC_SHA256(secret, b"PIDv3")[:32]. AES-256-CBC decrypt the voucher with its IV, PKCS#7-unpad → a KeySet → the SecretKey (DrmIonVoucher.decryptvoucher, ~lines 1643–1699).
  4. Decrypt the DRMION content with the SecretKey (AES-CBC) and repackage a DRM-free KFX. (Output is decrypted KFX, not EPUB — same limitation DeDRM has; a separate KFX→EPUB converter would be needed for a readable EPUB.)

What a Rust port needs:

  • A minimal Amazon-Ion binary reader (enough for the voucher + DRMION).
  • The YJ shared symbol table (the 2 MB kfxtables.py) embedded as a compressed include_bytes! blob, to resolve symbol IDs used in the voucher.
  • LZMA (lzma-rs) for compressed Ion LOBs — verify it handles KFX's framing.
  • Crypto we already have: AES-256-CBC (add 256-bit path to crypto.rs), HMAC-SHA256 (hmac crate), SHA-256, PKCS#7 unpad.

Risk: the 13-variant key derivation and the symbol-table embedding are fiddly, and "Amazon changes KFX DRM frequently" (DeDRM's own warning).

Background: .kinf2018 (closest documented relative of .kinf2024)

Reference: kindlekey.py::getDBfromFile, ~lines 419–572, and getK4Pids (kgenpids.py ~219–309). For version 6 / .kinf2018:

  • File = /-separated records; first record is a DPAPI-protected header (UnprotectHeaderData) decoded with testMap1, containing [Version:6][Build:b][Cksum:…][Guid:g].
  • Per-key derivation: salt = str(0x6D8*build) + guid, sp = GetUserName() + "+@#$%+" + GetIDString(), passwd = encode(SHA256(sp), charMap5), key = PBKDF2(passwd, salt, 10000, 0x400)[:32].
  • Each value: rearrange (prime-offset) → decode(testMap8) → iv||ciphertext, with iv = iv[:12] + 00 00 00 02, then AES-CTR decrypt → decode(charMap5).
  • GetIDString() = disk volume serial / MAC-derived machine id.

The useful outputs are kindle.account.tokens (account secret) and DSN (or the inputs to derive it: MazamaRandomNumber + IDString + UsernameHash). These feed both getK4Pids (for MOBI/AZW PIDs) and the KFX voucher lock params (CLIENT_ID=DSN, ACCOUNT_SECRET=token).

What's unknown for .kinf2024: the serialization (no / records), the header/version encoding, the per-value cipher and key derivation, and what machine/account entropy it binds to. All of this must be reverse-engineered.


What a follow-up needs to do

Part A — KFX decryption engine (port; ~the original Phase 4)

  1. Extend Format::detect to recognize a loose DRMION file (starts with \xeaDRMION\xee) as KFX, in addition to KFX-ZIP, and to recognize a Kindle content folder (.azw DRMION + *.voucher + *.res).
  2. Minimal Ion binary reader (kindle/kfx/ion.rs).
  3. Embedded YJ symbol table (kindle/kfx/symbols.rs, compressed blob).
  4. Voucher key derivation + DRMION decrypt (kindle/kfx/voucher.rs).
  5. Add AES-256-CBC + HMAC-SHA256 helpers to crypto.rs; add lzma-rs.
  6. Repackage decrypted KFX; wire the KfxZip/Kfx branch in decrypt.rs.

Part B — .kinf2024 key extraction (research; the real blocker)

  1. Decode the container. Figure out .kinf2024's framing (it is not /-delimited). Inspect for length-prefixes / a new separator / base-N blocks.
  2. Header + version. Find how the [Version:…][Build:…][Guid:…] (or its successor) is stored; confirm whether the header is still DPAPI-protected.
  3. Per-value crypto. Determine the v7+ key derivation and cipher (is it still PBKDF2 + AES-CTR? new salt/entropy? new char-maps?).
  4. Goal outputs: kindle.account.tokens and DSN (or MazamaRandomNumber
    • IDString + UsernameHash).
  5. Likely needs dynamic analysis of the Store Kindle app (the encoder is in the app), or watching for any community RE of .kinf2024.

Once Part B yields DSN + account secret, Part A can be validated on the Narnia book, and getK4Pids (already ported in kindle/pid.rs: encode, encode_hash, generate_device_pid) lets the existing MOBI path also decrypt account-locked classic .azw files.


DRMLibre changes already implied

  • crates/drmlibre-core/src/format.rs: add loose-DRMION detection + a Kfx (non-zip) variant; currently such a file returns "can't determine file type".
  • crates/drmlibre-core/src/kindle/pid.rs: encode/encode_hash/ generate_device_pid are ported and tested but #[allow(dead_code)] until getK4Pids (the Kindle-DB → PID path) is wired — that wiring is the natural companion to Part B.
  • crates/drmlibre-core/src/extract/: a kindle extractor would live here, alongside the working Windows Adobe extractor.

Open questions / risks

  • .kinf2024 may bind to hardware/account in new ways; extraction might only work on the same machine where Kindle is activated (acceptable for our use).
  • KFX DRM is a moving target; a port valid today may break on future books.
  • Output of Part A is decrypted KFX, not EPUB — readability needs a separate KFX→EPUB step (out of scope here).

References

  • DeDRM_tools: DeDRM_plugin/kfxdedrm.py, ion.py (DrmIonVoucher, obfuscate*/process_V*), kfxtables.py (symbol table), kindlekey.py (getDBfromFile, .kinf2018 v5/v6), kgenpids.py (getK4Pids).
  • Our notes on the KFX crypto core are in the project plan (~/.claude/plans/fizzy-sparking-wadler.md, Phase 4 section).