Commit Graph
12 Commits
Author SHA1 Message Date
JMR-devandClaude Opus 4.8 2cdefbe45c Update rand to 0.10 with a rand_core 0.6 compatibility shim
Bump the `rand` dev-dependency to the latest (0.10), which exposes rand_core
0.10, while the latest `rsa` (0.9) still requires an rand_core 0.6 RNG for key
generation. Bridge the skew with a small, test-only adapter instead of pinning
rand back.

* Add `rand_core_06 = { package = "rand_core", version = "0.6" }` so the shim
  can implement the old traits (cargo unifies it with the rand_core 0.6 that
  rsa already uses).
* `RandCompat<R>` wraps a modern RNG and re-implements rand_core 0.6's RngCore
  + CryptoRng over it, delegating to rand_core 0.10's infallible methods.
  Implementing both satisfies rand_core 0.6's blanket CryptoRngCore impl, which
  is exactly the bound rsa keygen requires.
* The CryptoRng bound is preserved (only wraps RNGs still marked
  cryptographically secure), so the randomness is not weakened — it is purely a
  trait-version shim. Tests now use `RandCompat(rand::rng())`.

46 tests pass (incl. the RSA-based EPUB roundtrips that exercise the shim);
clippy + rustfmt clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 20:44:22 -05:00
JMR-devandClaude Opus 4.8 259bb4d6fa Update dependencies to latest versions
Bump dependency requirements to their current latest and migrate the code for
the breaking API changes; refresh Cargo.lock for the rest.

Notable version bumps:
* zip 2 -> 8, quick-xml 0.37 -> 0.40, toml 0.8 -> 1
* windows 0.58 -> 0.62, winreg 0.52 -> 0.56
* RustCrypto: aes 0.8 -> 0.9, cbc 0.1 -> 0.2, sha1/sha2 0.10 -> 0.11,
  md-5 0.10 -> 0.11, ctr 0.9 -> 0.10, hmac 0.12 -> 0.13, pbkdf2 0.12 -> 0.13

Code migrations:
* quick-xml 0.40: BytesText::unescape() -> xml10_content();
  Attribute::unescape_value() -> normalized_value(XmlVersion::Implicit1_0)
* windows 0.62: LocalFree now takes Option<HLOCAL>
* cipher 0.5 (aes 0.9 / cbc 0.2): BlockEncryptMut/BlockDecryptMut ->
  BlockModeEncrypt/BlockModeDecrypt; encrypt_padded_mut/decrypt_padded_mut ->
  encrypt_padded/decrypt_padded

Held back deliberately: rand stays 0.8 because the latest rsa (0.9) still needs
an rand_core 0.6 RNG for keygen, which rand 0.9+ no longer provides. rsa pinning
the older RustCrypto generation also leaves a couple of duplicate transitive
versions (digest 0.10/0.11, crypto-common 0.1/0.2) — harmless.

Verified: 46 tests pass, clippy + rustfmt clean, and a real ADEPT EPUB still
decrypts end-to-end (DPAPI key extraction + RSA/AES) to a valid DRM-free file.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 20:27:43 -05:00
JMR-dev 2d8f3f910d date comment update 2026-06-24 20:13:08 -05:00
JMR-devandClaude Opus 4.8 47041225a0 Pin CI/release Rust toolchain to 1.96.0 (latest stable)
Replace the floating `stable` channel with the exact current latest stable
(1.96.0, which also matches the crate's rust-version/MSRV) so builds are
reproducible and `clippy -D warnings` can't break from an unrelated toolchain
bump.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 20:11:24 -05:00
JMR-devandClaude Opus 4.8 f776172d57 Pin GitHub Actions to commit SHAs at their latest versions
Update each action to its current latest release and pin to the full commit
SHA (supply-chain hardening), with the version in a trailing comment:

* actions/checkout            v4 -> v7.0.0
* Swatinem/rust-cache         v2 -> v2.9.1
* actions/upload-artifact     v4 -> v7.0.1
* actions/download-artifact   v4 -> v8.0.1
* softprops/action-gh-release v2 -> v3.0.1
* dtolnay/rust-toolchain      pinned to master @ 2026-06-20 (no tagged
  releases); add explicit `toolchain: stable` since the channel can no
  longer be inferred from the @ref once pinned to a SHA.

Verified upload-artifact v7 (zips by default) and download-artifact v8
interoperate, and that every input still exists in the new majors.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 20:08:40 -05:00
JMR-devandClaude Opus 4.8 e5e4173ee6 Add CI and release GitHub Actions workflows
* ci.yml: on pull requests to main, runs on windows-latest and macos-latest;
  checks formatting, runs clippy (warnings denied), and runs the unit tests
  (`cargo test --workspace`).
* release.yml: manual workflow_dispatch (tag + optional prerelease inputs);
  builds release binaries for Windows (x86_64) and macOS (aarch64 + x86_64),
  then publishes them to GitHub Releases with auto-generated notes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 20:03:32 -05:00
JMR-devandClaude Opus 4.8 01334d31d0 Fix panic-on-malformed-input defects found in code review
Harden the decryptors against corrupt/edge-case input so a single bad file
errors cleanly instead of panicking (and, in a batch, aborting the whole run):

* ADEPT EPUB: the content-decrypt guard was `len < 16` but then sliced off the
  16-byte pad block before reading the trailing pad length; a one-block (16-byte)
  ciphertext left an empty slice and panicked on `.last().unwrap()`. Guard is
  now `len <= 16`.
* MOBI: validate the section table before indexing it — require >= 2 sections
  and reject a record count that exceeds the section count, preventing
  out-of-bounds panics on `section_offsets[1]` / `section_bounds(i)`.
* MOBI: `trailing_size` now uses saturating/checked subtraction, and the caller
  clamps the trailing size to the record length, so a corrupt trailing-size
  encoding can't underflow.
* MOBI: `normalize_pids` slices PIDs by characters, not bytes, so a non-ASCII
  `--pid` can't panic on a non-char-boundary.
* CLI: wrap the per-file decrypt in `catch_unwind` so any future panic is
  contained to that file rather than aborting a multi-file run.

Adds 5 regression tests (one per fix). 46 lib tests pass; clippy + fmt clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 19:57:52 -05:00
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
JMR-devandClaude Opus 4.8 c15b3131a5 Document Store-Kindle key vault + book census (account-locked, .kinf2024)
Investigated decrypting the My Kindle Content library and automating Kindle
key extraction. Census: 14 KFX, 1 Topaz, 1 MOBI (crypto_type 2) — all 16
account-locked, none decryptable without the Kindle account key.

That key is only in the modern Store/UWP vault: .kinf2024 (char-encoded, no
"/" records) plus main_shared.blob/.salt/.sha256. The blob is NOT a DPAPI blob
and there is no registry entry, no .kinf2018, and no plaintext token file, so
the key cannot be recovered by static file inspection — it would need dynamic
analysis of the UWP app. The realistic workaround (use Kindle for PC <=1.39,
which writes .kinf2018) and the achievable port work it would unlock are
recorded in docs/kfx2024-decryption-effort.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 19:12:57 -05:00
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
JMR-devandClaude Opus 4.8 2dcfcf5631 Add Windows Adobe key auto-extraction (phase 3, part 1)
On Windows, recover the Adobe ADEPT user key from an installed/activated
Adobe Digital Editions and use it automatically, so ADEPT EPUBs decrypt
with no --key argument. Ported from adobekey.py:

* Build the DPAPI entropy byte-for-byte: system-volume serial number,
  CPUID leaf-0 vendor (EBX|EDX|ECX) and leaf-1 signature, and the ADE
  username, packed as ">I12s3s13s".
* CryptUnprotectData (DPAPI) recovers the key-encryption-key from the
  HKCU\Software\Adobe\Adept\Device "key" value, then each per-credential
  privateLicenseKey under ...\Activation is AES-128-CBC decrypted and the
  DER RSA key taken from byte 26 onward.

Decryption now lazily auto-extracts and retries when no supplied key fits,
and newly found keys are written back to the TOML config so later runs are
offline. Uses the `windows` (DPAPI/volume info) and `winreg` crates behind
cfg(windows); pure-Rust crypto unchanged.

Verified end-to-end on a real ADEPT EPUB: the key was extracted from the
local ADE and the book decrypted to a valid, DRM-free EPUB (rights.xml and
encryption.xml removed, content readable).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 18:48:37 -05:00
JMR-devandClaude Opus 4.8 409473b8f3 Initial release: ADEPT EPUB and Kindle MOBI DRM removal (phases 0-2)
DRMLibre is a standalone, single-binary Rust CLI that removes DRM from
Amazon Kindle and Adobe Digital Editions ebooks. The decryption logic is
ported from DeDRM_tools (GPLv3); this project is GPL-3.0-or-later.

This commit covers the first three milestones of the plan:

* Phase 0 - workspace scaffold (drmlibre-core engine + drmlibre-cli),
  native TOML config, magic-byte/zip-content format detection, and the
  remove/passhash/config CLI surface with atomic output handling.
* Phase 1 - Adobe ADEPT EPUB: RSA (PKCS#1 v1.5) and PassHash book-key
  unwrap, RMSDK>=10 hardening, AES-128-CBC content decryption, and IETF/
  Adobe font de-obfuscation. Verified end-to-end with a real RSA key.
* Phase 2 - Kindle MOBI/AZW/AZW3: PC1 cipher, type-1 and type-2 DRM,
  serial->PID derivation, EXTH header patches, and trailing-data handling.
  Verified end-to-end and against the upstream golden vectors.

All crypto is pure Rust (RustCrypto), so the release binary has no
third-party runtime dependencies. 42 tests pass; clippy and rustfmt clean.

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