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>
This commit is contained in:
@@ -0,0 +1,190 @@
|
|||||||
|
# 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).
|
||||||
Reference in New Issue
Block a user