Commit Graph
43 Commits
Author SHA1 Message Date
Matthew Jackson d638296e01 keydb_format: add to_keydb_cfg serializer (inverse of parse); rename DiscEntry.disc_id -> vid
Central round-trippable codec: parse + to_keydb_cfg in one place.
Emits HC, DK, PK, then disc entries sorted by hash. Round-trip test
(parse -> serialize -> re-parse) covers HC/DK/PK/disc. disc_id -> vid
names the field for what it is (the I-field volume ID).
2026-06-27 14:42:58 -07:00
Matthew Jackson 880830e262 keysources: expose the keydb.cfg parser (pub keydb_format / KeyDb, DiscEntry)
leak-guard / leak-guard (push) Failing after 20s
Make the parser public — parsing a keydb is not secret (freemkv uses it, and so
do tools that build a per-disc registry from it, e.g. a Volume-ID index). Purely
additive: a private module in this public crate becomes nameable; no behaviour
change, nothing removed.
2026-06-26 21:07:56 -07:00
Matthew Jackson 17fcf6d8f9 KeydbSource owns keydb save + update
Move the keydb save/validation/atomic-write path out of libfreemkv onto
KeydbSource. New KeydbSource::save(bytes) validates + decompresses (zip /
gz / plain, decompressed-size capped) and crash-safely writes to the
source's OWN path (sibling-temp + fsync + rename + parent-dir fsync) —
not a hardcoded default, so the caller chooses the destination.
KeydbSource::update(fetch, url) calls an INJECTED fetch closure then
save, keeping this crate transport-agnostic on the update path (the app
supplies its own TLS / SSRF-guarded transport). UpdateResult moves here
and is re-exported. Add flate2 + zip (already in the resolved graph via
libfreemkv) for decompression; no new HTTP stack.
2026-06-26 17:34:03 -07:00
Matthew Jackson 946632d9fb Restore AACS PK key-processing path in keysources
unit_keys_from now resolves the Media Key in order: stored per-disc MK
-> keydb Processing Key pool (mk_from_pk vs this disc's own MKB) ->
device-key pool (mk_from_dk), then MK+VID -> VUK -> UK. MK/VUK entries
still honored directly; cross-disc MK-pool brute stays retired. Fixes the
factually-wrong justifying comment + adds PK-pool KATs.
2026-06-26 17:03:58 -07:00
Matthew Jackson 6805ad22d4 AACS: own keydb parser + 100% parse + get_uk derivation
- Relocate keydb.cfg parser into keydb_format.rs (libfreemkv no longer knows
  keydb); add 100% parse (mkb_version/volume_size/is_uhd, revoked_at_mkb) +
  helper API (get_uk/get_uks/get_vid/host_certs(mkb)).
- KeydbSource/OnlineSource/MultiSource -> get_uk(ctx); MultiSource host_certs
  union; KAT-proven derivation parity. NumberedUnitKey alias. clippy clean.
2026-06-26 12:19:24 -07:00
matthew d1a4ec9a3f ci: add CI workflow (fmt + clippy -D warnings + test on Rust 1.86)
The crate previously had only leak-guard.yml and release.yml, so its ~42
tests never ran on push/PR — only on tag. Mirror the sibling crates'
ci.yml (lint + test jobs, dtolnay/rust-toolchain@1.86.0, rust-cache).
No --locked: this library does not track Cargo.lock (release.yml notes
--locked would fail on a fresh runner).
2026-06-25 17:58:30 -07:00
Matthew Jackson 8e166903cc freemkv-keysources: fixture-based integration tests for key sources
Add tests/key_sources.rs exercising the published KeySource impls
end-to-end over real fixture files and the libfreemkv parsers:

- KeydbSource: disc-hash lookup from a real keydb.cfg (VUK hit ranks
  ahead of the universal DK pool), hash miss yields only the pool,
  missing file is silent/not errored, label + needs_samples, and
  host-cert serving from a | HC | row (inherent and trait paths).
- paths: exe-local search list, default == search head, existing path
  reflects on-disk state (local-only, no OS fallback).
- MapfileSource: persisted # freemkv-uk: keys read back as a terminal
  Key::Unit, one-shot exhaustion, missing/keyless mapfile offers nothing.
- OnlineSource: unconfigured no-op (no network), one-shot latch,
  metadata, and validate_keyserver_url scheme/SSRF gating.
- MultiSource: caller-supplied order/precedence (and its reverse),
  empty-source skip, needs_samples/errored OR-aggregation, nesting, and
  a real keydb-then-mapfile precedence chain over fixtures.
2026-06-24 23:33:00 -07:00
Matthew Jackson 699a2872fb v1.0.0-rc.5.3: bump version (unified release) 2026-06-24 21:55:08 -07:00
Matthew Jackson ed744e071f freemkv-keysources: keydb search is local to the executable
keydb_search_paths() returns exactly [<exe dir>/keydb.cfg] (or empty if
current_exe is unavailable) — no %APPDATA%/.config/XDG lookup. Matches
libfreemkv::keydb::default_path.
2026-06-24 20:46:31 -07:00
Matthew Jackson 2d0d0e3966 v1.0.0-rc.5.2: bump version (unified release) 2026-06-24 17:19:48 -07:00
Matthew Jackson 91b95e15cf freemkv-keysources: pure key lookup
Move the encrypted sample reader and the key-resolution loop into libfreemkv
(they read the disc and validate keys — decryption mechanism, not lookup). A
key source now only looks a key up and hands it back.
2026-06-24 15:40:50 -07:00
Matthew Jackson a4d94d4ce3 v1.0.0-rc.5.1: bump version (unified release) 2026-06-24 14:43:34 -07:00
Matthew Jackson 1976416b14 Add Contributor Covenant v2.1 Code of Conduct 2026-06-24 10:44:59 -07:00
Matthew Jackson 2b025ab5fe v1.0.0-rc.5: bump version (unified release) 2026-06-24 10:35:11 -07:00
Matthew Jackson cced7818d2 v1.0.0-rc.4.3: bump version (unified release) 2026-06-23 15:53:02 -07:00
Matthew Jackson c247f02894 v1.0.0-rc.4.2: bump version (unified release) 2026-06-23 12:41:44 -07:00
Matthew Jackson a35f3cf84a v1.0.0-rc.4.1: bump version (unified release) 2026-06-23 10:46:48 -07:00
Matthew Jackson 9ada82a415 ci: cache Rust builds with Swatinem/rust-cache to speed up release + CI 2026-06-23 10:01:37 -07:00
Matthew Jackson 26f4c864b5 v1.0.0-rc.4: bump version (unified release) 2026-06-23 09:46:59 -07:00
Matthew Jackson a287e165bd keysources: label() on keydb/online sources; neutral path in the XDG test 2026-06-23 09:09:06 -07:00
Matthew Jackson 7982d16225 ci: fast-release — verify Cargo.lock libfreemkv resolves to the release git tag 2026-06-23 08:20:51 -07:00
Matthew Jackson a8be43ea69 keydb: require content samples so per-disc UK entries are ciphertext-validated
A keydb can hand out a per-disc terminal Key::Unit (a UK entry keyed on
disc_hash). Unlike a derived key (Device/Processing/Media/Volume), a
terminal UK is applied as-is by Disc::decrypt_with: it is NOT re-derived
through the MKB-verified AACS resolver, so a UK entry whose hash matches
the disc but whose key bytes are wrong is only disproved by descrambling
real ciphertext.

KeydbSource inherited the default needs_samples() == false, so on the
autorip auto-resume / mux-worker path (which samples units only when some
source reports needs_samples()) a keydb-only resolve ran with empty
samples and committed a wrong UK as success, muxing undecryptable video
while reporting done. The CLI was unaffected because it always samples.

Override needs_samples() to true on KeydbSource so every consumer samples
encrypted units before resolving and a wrong keydb UK is rejected on all
paths. Regression test asserts the override.
2026-06-23 01:53:36 -07:00
Matthew Jackson 6f164ca355 v1.0.0-rc.3.1: online key-service auth header + cross-OS keydb search paths (paths.rs) 2026-06-22 18:09:48 -07:00
Matthew Jackson 2987792328 v1.0.0-rc.3: bump version 2026-06-22 16:19:11 -07:00
Matthew Jackson bde416604e keysources: expose host certs through KeySource trait
- KeydbSource implements KeySource::host_certs(), delegating to the
  inherent host_certs() — surfaces the | HC | / | HC2 | certs already
  parsed from keydb.cfg by libfreemkv's parser, so the OEM cert route
  collects them across the keysource layer. No new parsing.
- OnlineSource::host_certs() is a no-op stub: returns empty with zero
  network access (no client fetch, no server endpoint). Online host-cert
  serving is deferred. TODO(owner) marker left in place.

Tests: trait host_certs returns the keydb HC row; empty when keydb
missing; online host_certs is an empty no-op without network.
2026-06-22 11:23:46 -07:00
Matthew Jackson 268e1d6bf2 v1.0.0-rc.2: bump version (unified release with libfreemkv 1.0.0-rc.2) 2026-06-22 09:17:07 -07:00
Matthew Jackson cfb90514e9 ci: reword publish note (drop internal-repo reference) 2026-06-22 09:15:17 -07:00
Matthew Jackson c65cbe7f34 ci: add Release workflow (verify + test + GitHub release; crates.io publish driven by release.sh) 2026-06-22 08:54:25 -07:00
Matthew Jackson 0139ba6f1b rc2: SSRF parity, bounded DNS, strict hex parse, over-cap MKB error signaling 2026-06-22 08:47:52 -07:00
Matthew Jackson 3957ac70c1 v1.0.0-rc.1
key sources (keydb/online/mapfile), overflow-safe LBA
2026-06-21 21:06:07 -07:00
Matthew Jackson a4e8fbe06c docs: generic wording in keydb comment (drop specific title) 2026-06-07 21:27:06 -07:00
Matthew Jackson d23fcbd094 0.31.0: build against libfreemkv 0.31; add self-contained leak-guard CI; thin LTO release profile 2026-06-07 17:53:23 -07:00
MattJackson 68a2e51bf7 v0.30.0: OnlineSource forwards the disc title to the key service
OnlineSource::query() now includes the disc's volume_label (UDF/ISO volume id)
as a plain-text `title` field in the /decode POST, so the key service can build
a disc_hash → title catalog from real rips. Depends on libfreemkv 0.30.
2026-06-06 09:05:48 -07:00
MattJackson f02a62595d v0.29.0: bump version + libfreemkv 0.29 2026-06-05 20:43:54 -07:00
MattJackson da5bd08d3f sources: stateful one-key-at-a-time providers, UK-first keydb, shared resolve loop
Each source implements next_key (a cursor over its candidates) instead of
returning them all at once. The keydb hands its per-disc candidates out
UK-first (UK > VK > MK > DK) so a stale/wrong VUK never pre-empts a good UK in
the same entry; online and mapfile are one-shot. MultiSource composes sources
in the caller's chosen order and resolve_and_apply drives the
next_key -> decrypt_with loop, stopping at the first key that decrypts.
read_sample_units moves here so the CLI and autorip share one content sampler.
2026-06-05 09:23:17 -07:00
MattJackson e3f452166b 0.28.0: pin libfreemkv to crates.io 0.28 (drop dev path) 2026-06-04 18:24:51 -07:00
MattJackson a4c6a4123a 0.28.0: version bump (keydb decoupling) 2026-06-04 16:41:04 -07:00
MattJackson b5a18598ed host_certs: negative test only (no key-shaped material in code) 2026-06-04 16:38:58 -07:00
MattJackson 309621ac45 KeydbSource::host_certs() — the keydb's second data type, for DriveCredentials 2026-06-04 16:04:06 -07:00
MattJackson c6f1ec1fcc OnlineSource: minimal client; POST to the configured URL verbatim
Drop unused tracing dep. The endpoint URL is taken from config as-is, so a
change on the service side is a config change, not a code change.
2026-06-04 15:33:34 -07:00
MattJackson 0e65237ce7 OnlineSource::needs_samples() = true (validates against ciphertext) 2026-06-04 14:45:49 -07:00
MattJackson 681e7a0295 Add OnlineSource and MapfileSource
OnlineSource: the remote key-service client (moved out of autorip), posting the
disc's Unit_Key_RO.inf + MKB + Volume ID + encrypted content samples to the
service and returning the resolved unit key as a terminal Key::Unit candidate.
Kept out of libfreemkv so the library stays network-free. Source-internal
failures (unreachable / status / parse) are logged and surface as "no
candidate" so the next source is tried.

MapfileSource: reads a rip mapfile's persisted unit keys (the resume / deferred-
mux fast path) and offers them as a Key::Unit candidate. Keyed by mapfile path.

DiscInputs gained an app-populated `samples` field for sources that validate
against ciphertext server-side (OnlineSource); local sources ignore it.
2026-06-04 14:42:02 -07:00
MattJackson d2e0ecc2d4 freemkv-keysources: new crate — KeydbSource + ordered-resolve helper
The published key-source layer for libfreemkv. libfreemkv does no lookup; it
is handed a Key and derives down. This crate provides the KeySource impls that
do the lookup and hand a Key in. Applications choose and order the sources.

This first cut ships:
- KeydbSource: parses a local keydb.cfg and enumerates its material as ordered
  candidate keys (per-disc VUK/unit/media first, then the universal device-key,
  processing-key, and media-key pools). It does no derivation — the library
  walks the MKB and verifies media keys. Candidate ordering lets the library
  try each path a keydb can satisfy.
- resolve_first: tries each source's candidates in order and returns the first
  the caller's validator accepts (validate-before-return), so a stale entry
  falls through to the next source.

OnlineSource (remote key service) and MapfileSource (cached unit key) land with
the application wiring, where the sample-read and mapfile paths already live.
2026-06-04 14:35:54 -07:00