Files
DRMLibre/docs/kfx2024-decryption-effort.md
T
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

259 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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).