Files
DRMLibre/docs/kfx2024-decryption-effort.md
JMR-devandClaude Opus 4.8 9ea5a05ac6 Scrub personal book identifiers from findings doc before publishing
Replace specific ASINs, the book title, and the license voucher GUID with
generic placeholders ahead of making the repo public. No technical content
changes (the format census, vault structure, and analysis are unaffected).

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

13 KiB
Raw Permalink 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 (a KFX title from a local Kindle library) 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\<ASIN>_EBOK\)

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

First 16 bytes of <ASIN>_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).

Update — Store/UWP Kindle key vault + book census (same investigation)

Follow-up attempt to decrypt the user's My Kindle Content library and automate Kindle key extraction. Result: same blocker, now confirmed harder.

Every book is account-locked

Census of ~\My Drive\Documents\My Kindle Content\*_EBOK\*.azw (16 books):

Format Count Notes
KFX (DRMION) 14 needs voucher key (account DSN/secret)
Topaz (TPZ0) 1 different cipher, out of v0.1 scope
MOBI 1 crypto_type 2 (HUFF/CDIC), needs a PID

So none are decryptable without the Kindle account key — even the single MOBI is PID-locked (not the fixed-key type-1 we hoped for).

The Store/UWP key vault (…\Classic\Data\storage\)

File Size Observation
.kinf2024 86,477 char-map-encoded; no / records (unlike .kinf2018)
main_shared.blob 256 not a DPAPI blob (starts a8 90 2d 20…, not 01 00 00 00 d0 8c 9d df)
main_shared.salt 32 salt for deriving the blob's key
main_shared.blob.sha256 32 integrity hash

main_shared.salt + a custom-encrypted main_shared.blob is a new master-key vault: the blob's encryption key is derived inside the UWP Kindle app (not via DPAPI), so it can't be recovered by reading files. .kinf2024's per-value protection presumably chains off this master key.

Accessible-credential paths — all exhausted

  • HKCU\Software\Amazon: absent (UWP app doesn't use it).
  • main_shared.blob: not DPAPI → CryptUnprotectData doesn't apply.
  • No .kinf2018/.kinf2011 anywhere (no older desktop K4PC install).
  • No plaintext kindle.info/token files.

Conclusion: extracting the account key from this machine's Store Kindle is not feasible by static file inspection. It would require dynamic analysis of the running UWP app (hooking the DataProtection/key-derivation calls or dumping the decrypted master key from memory) — specialized RE, uncertain, and outside what the available tooling can do.

Realistic ways to actually decrypt these books

  1. Use a Kindle version DeDRM supports. Kindle for PC ≤ 1.39 (the desktop .exe, not the Store app) stores keys in .kinf2018 and downloads MOBI/older-KFX. De-register the Store app, install the older client, re-download, and the key is then extractable. This is the standard community workaround for the Store-app/.kinf2024 problem.
  2. A registered eInk Kindle's serial number lets getKindlePids (already ported, kindle/pid.rs) derive PIDs for books delivered to that device — though app/account downloads usually won't match.
  3. Wait for / import community RE of the .kinf2024 + main_shared vault.

What DRMLibre can do to support path (1) — a port, achievable

  • Port kindlekey.py .kinf2018 extractor (DPAPI header unprotect, the testMap1/testMap8/charMap5 decoders, GetIDString = volume serial [we already have volume_serial()], PBKDF2, AES-CTR). → produces the kindle key DB.
  • Port kgenpids.py getK4Pids (primitives encode/encode_hash/ generate_device_pid already ported + tested in kindle/pid.rs).
  • Wire the kindle DB → PIDs → existing MOBI decryptor; add Topaz + KFX engines.

This would NOT decrypt the books currently on this machine (they're .kinf2024), but would make the tool "just work" against a supported Kindle install.

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).