# 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\_EBOK\`) | File | Size | Notes | |---|---|---| | `_EBOK.azw` | 1,166,844 | **DRMION container** (see magic below) | | `amzn1.drm-voucher.v1..voucher` | 1,199 | Encrypted DRM voucher (holds the content key) | | `CR!….azw.res` (many) | 1–6 MB each | KFX resource containers (images/fonts/text) | | `_EBOK.mbpV2` | 554 | annotations/sidecar | | `_EBOK.phl` | 1,129 | popular-highlights sidecar | First 16 bytes of `_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).