Commit Graph
235 Commits
Author SHA1 Message Date
Matthew Jackson 940bb215ab online: follow libfreemkv's halt::Progress rename to Liveness v1.7.7-rc4 v1.7.7-rc3 2026-09-30 20:36:55 -07:00
Matthew Jackson 18b590407c Cargo.lock: restore (accidentally changed by local patch) 2026-09-30 20:24:57 -07:00
Matthew Jackson 116b33739b tests: halt-landing tests assert Halted, not a tight wall-clock bound 2026-09-30 20:24:46 -07:00
Matthew Jackson d935fa6ffd online: allow LAN/loopback/link-local key-service hosts, reuse libfreemkv is_blocked_ip 2026-09-30 20:03:38 -07:00
Matthew Jackson b65959d3a1 ci: run cargo-deny from the repo dir so the .cargo patches apply 2026-09-30 16:09:03 -07:00
Matthew Jackson 3f08e980a0 ci: path-patch freemkv-unlock; dependabot ignores org workflow refs
libfreemkv now deps freemkv-unlock by version with a git-tag override, so every root that reaches it patches it. Dependabot config unified; it no longer proposes freemkv/.github v1->v2.
2026-09-30 15:53:05 -07:00
Matthew Jackson 6c8e2c5c46 Allow forensic phase fallback after definitive key service misses 2026-09-29 17:20:43 -07:00
Matthew Jackson ba357e46f5 online: wire ctx.halt()/ctx.progress() into the query worker (ST-K1b) 2026-09-28 08:18:38 -07:00
Matthew Jackson a815a0a578 online: ST-K1b tests for ctx.halt()/ctx.progress() wiring (RED) 2026-09-28 08:15:20 -07:00
Matthew Jackson ba6f213be3 KU-E1b: the key-service query is not yet Stop-aware (wording)
check_keyserver_url_static's rustdoc and its test comment no longer
call the first query halt-aware: OnlineSource runs it under its own
Halt until ST-K1b wires ctx.halt() (J10).
2026-09-28 07:18:27 -07:00
Matthew Jackson 37568b1002 test: use the RFC 2606 .test TLD for the unresolvable host (leak-guard flags .invalid) 2026-09-28 07:04:48 -07:00
Matthew Jackson 40389acd3f KU-E1 review minor 4: partial stored keys plus a Km still report NoVid
The keydb keeps a NoVid miss reason beside the keys it did return, so
resolve sets vid_would_help for the CPS units only the VID could
derive. Other miss reasons are still reported only with no key.
2026-09-28 06:28:54 -07:00
Matthew Jackson 54508d8ec2 KU-E1 review minor 4 red: partial stored keys plus a Km still report NoVid
A keydb entry with stored unit keys for some CPS units and a Media Key
but no VID returns the stored keys with an empty miss path, so
resolve cannot tell that the VID would derive the rest (J23).
2026-09-28 06:28:47 -07:00
Matthew Jackson 2c93420970 KU-E1: a DNS-free key-service URL check for factory builds
check_keyserver_url_static rejects every Config fault (scheme, host,
port, a literal non-public address) with no lookup. A factory build,
which no Stop can reach, uses it; the host lookup and its SSRF guard
run at the first query on the halt-aware worker, as they already did.
A test-only counter proves the check starts no lookup.
2026-09-28 06:24:52 -07:00
Matthew Jackson c48faa042b KU-E1 red: a DNS-free key-service URL check for factory builds
The engine checks the key-service URL, a DNS lookup with a fixed 10 s
timeout that Stop cannot interrupt, on every factory build, and no
Halt reaches a factory. The test asks for a static check that rejects
Config faults with no lookup.
2026-09-28 05:45:58 -07:00
Matthew Jackson ab93818012 KU-E1 J23: OnlineSource and MultiSource report uses_vid
OnlineSource::uses_vid is true (it sends vid_b64); MultiSource is
any(inner); KeydbSource keeps the default false.
2026-09-28 05:31:51 -07:00
Matthew Jackson f3b66307cc KU-E1 J23 red: which key sources consume the disc's VID
OnlineSource sends the VID (vid_b64), so it uses it; MultiSource uses
it if any inner source does; KeydbSource does not (its Km-without-VID
case is reported per disc, as a NoVid miss path).
2026-09-28 05:31:23 -07:00
Matthew Jackson ad59f3ce45 KU-K1 review: peek the reachability slot, MultiSource forwards last_failure
record_last_failure_transport now reads LAST_DECODE_REACHABILITY.with(|c|
c.get()) directly instead of take_last_decode_reachability(), so it no longer
erases the slot freemkv-library's server reads after resolve
(server/ripper/mod.rs:505, :1361) to avoid a second key-service call. Green:
the regression test now finds the verdict still in the slot after
get_unit_keys/get_fmts_indexes. MultiSource::last_failure_was_transport
forwards any(inner), matching answer_depends_on_samples (KU3-6).
2026-09-28 04:19:04 -07:00
Matthew Jackson 4e83b3c1f8 KU-K1 review: red — record_last_failure_transport erases the reachability slot
Opus's review: freemkv-library's server calls take_online_decode_reachability()
after resolve (server/ripper/mod.rs:505, :1361) to classify a no-key without a
second probe. record_last_failure_transport currently TAKES the thread-local
slot, so the server sees None and fires probe_online_reachability — a second
key-service call, and a definite 422 misread as an outage. Red: the slot is
gone after get_unit_keys/get_fmts_indexes. Also adds the forensic-path,
mid-body-stall, transport-then-5xx-reset and MultiSource forwarding tests
requested in review; the latter fails since MultiSource has no override yet.
2026-09-28 04:18:02 -07:00
Matthew Jackson c5985d1c87 KU-K1: keydb/multi sample-dependence and online transport-class failures
Per keys-upfront-design KU-K1 (§8.2): KeydbSource::answer_depends_on_samples
is false (keyed by disc hash, asked once per resolve, N-KU10); MultiSource's
is any(inner) (KU3-6), so a nested sample-dependent source still gets the
per-piece ask. OnlineSource::last_failure_was_transport (J15) is true only
when the most recent query recorded DecodeReachability::Transport (DNS,
connect refused, or an idle stall — never an HTTP answer, 5xx included), and
resets on the next success, so resolve's J13 retry (stop-design-v5 T31)
targets exactly the requests that never got an answer.
2026-09-28 04:13:03 -07:00
Matthew Jackson 8f923fd5f9 KU-K1: tests for answer_depends_on_samples and last_failure_was_transport
KeydbSource::answer_depends_on_samples (KSK1) and MultiSource::answer_depends_on_samples
(KSK2, KU3-6 "any(inner)") both default to `true` today, so a keydb-then-online
composition would be re-asked per piece instead of once. OnlineSource::last_failure_was_transport
(J15) defaults to `false`, so a transient DNS/connect/idle failure would never be
retried by `resolve` (J13). Red: these six assertions fail against today's defaults.
2026-09-28 04:12:12 -07:00
Matthew Jackson 18fdab6b77 KU-P1: decrypt the real-unit test through test_util::decrypt_unit
aacs::content::decrypt_unit becomes pub(crate) at KU-X2 (keys-upfront design
v3.4 §2.2); the ignored real-unit test now uses the test-util door. The
feature comes in as a dev-dependency (resolved through the sibling path patch).
2026-09-28 00:22:08 -07:00
Matthew Jackson dfa02adbe2 online: a stopped key query never sends and builds no body
Review fixes, green half:
- PostJob carries the caller's Halt and re-checks it after the host lookup,
  before connecting. A Stop during DNS then never uploads the key material
  or bearer token, so a reopen's query is the only one the service sees.
- query_with returns Halted before reading or base64-encoding the MKB when
  a Stop is already pending.
2026-09-27 23:50:33 -07:00
Matthew Jackson cbab284c85 online: tests that a stopped key query sends nothing and reads no disc material
Review fixes, red half: a Stop during the host lookup must never reach the
service (never call the key service twice), and a pending Stop returns
before the body is built. KT7a's u64::MAX check now asserts saturation.
2026-09-27 23:49:08 -07:00
Matthew Jackson 0ffdb2a9f6 online: stall-only key-service timeouts and mid-flight Stop (ST-K1a)
Commit 2 of 2 (stop-design-v5 §2.7, T15-T17, D3, D4):

- query_with builds the body on the caller's thread and runs DNS + POST on
  a worker, capped at 4 per host. The caller waits for a slot and for the
  result halt-aware in 20 ms slices, so Stop returns Halted within a slice
  at any stage. The abandoned worker frees its slot at its own stall bound.
- Reachability is recorded on the caller's thread, only when the worker's
  result arrives. A Stop records nothing.
- IdleConnector, chained after DefaultConnector, owns every post-connect
  bound. Each write and read gets a fresh 60 s stall bound (T16), and the
  wait for a reply's first byte gets 60 s + request bytes / 256 KiB/s (T17).
  The 180 s phase totals are gone: no total is configured.
- A reply body that stalls or drops records Transport reachability (T16).
  An over-cap body still records its Status.

The trait path passes a fresh Halt. ctx.halt()/ctx.progress() is ST-K1b,
after KU-X2. No public API change.
2026-09-27 23:41:43 -07:00
Matthew Jackson adbb1a12a0 online: ST-K1a tests for stall-only key-service timeouts and mid-flight Stop
Commit 1 of 2 (stop-design-v5 §5.0): KT1-KT10 plus guards G12/G13, with
signatures that keep today's behaviour (query_with ignores the halt,
hardened_agent_with maps idle onto the old phase totals, the first-byte
budget does not scale), so each test compiles and fails on its assertion.
KT9 replaces the 180 s totals test. The ctx.halt()/ctx.progress() wiring
is ST-K1b (after KU-X2).
2026-09-27 23:37:22 -07:00
Matthew Jackson 05281249d1 keydb: hand out host certs best first, with no disc input
Unannotated certs in keydb order, then revoked ones by latest revocation.
The trait ignores its mkb hint; KeyDb::host_certs(mkb) is deprecated.
2026-09-27 17:24:04 -07:00
Matthew Jackson cedbc4b1fa online: offline Temporary-verdict test; proxy wording
Drop the real-DNS .invalid test for an offline check of every GuardFail::Unreachable reason; say every key lookup failed.
2026-09-27 16:44:34 -07:00
Matthew Jackson 1606ff3ca3 online: review nits for the proxy fix and keyserver URL verdict
Correct the rationale: the env proxy was resolved to the key service's pinned address, so every lookup failed.
Gate set_last_decode_reachability behind a test-hooks feature, mark the verdict types non_exhaustive, test a .invalid host, add CHANGELOG.
2026-09-27 12:40:09 -07:00
Matthew Jackson 10b0348db8 online: never use an env proxy; typed keyserver URL verdict and reachability test hook
The key-service agent disables ureq's env proxy, which would re-resolve the host and bypass the pinned, SSRF-guarded addresses.
check_keyserver_url returns a Permanent/Temporary KeyserverUrlRejection; set_last_decode_reachability plants a verdict for caller tests.
2026-09-27 12:26:39 -07:00
Matthew Jackson d0a3ab852b v1.7.7: bump version (unified release) v1.7.7 v1.7.7-rc2 2026-09-26 21:16:59 +00:00
Matthew Jackson 91b906fa61 Prepare 1.7.7 with concise comments and shared CI policy v1.7.7-rc1 2026-09-26 11:46:35 -07:00
Matthew Jackson e987506d96 v1.7.6: bump version (unified release) v1.7.6 v1.7.6-rc4 2026-09-26 04:05:43 +00:00
Matthew Jackson 60cff0ead6 chore: absorb qa into dev for lockstep promote [skip-media] v1.7.6-rc3 2026-09-25 12:25:47 -07:00
Matthew Jackson c333c6000b v1.7.6: bump version (unified release) v1.7.6-rc2 2026-09-25 05:32:01 +00:00
Matthew Jackson adf2401c06 chore(qa): gate 1.7.6 promote — media matrix not needed [skip-media]
The 1.7.6 unified release ships no rip-path change (Linux desktop shell,
notifications, menu_layout refactor, XDG paths). Real-media QA on EC2
adds ~2h + real money to the release cycle to prove code that never
enters the rip pipeline. This empty commit puts `[skip-media]` in qa's
HEAD so the media matrix in qa.yml auto-skips per the documented bypass;
the full-platform builds/tests still run.
v1.7.6-rc1
2026-09-24 21:37:29 -07:00
Matthew Jackson b89b7c9660 chore(qa-sync): absorb qa's post-1.7.5 release commits into dev
Release-orchestrate committed to qa (v1.7.5 version bump, etc.) but did
not push those commits back to dev, so promote's fast-forward gate
refused (qa was diverged from dev). This is an ours-merge: the commit
records qa as an ancestor of dev without applying its content, since
dev has already moved past 1.7.5 to 1.7.6 with the equivalent
information in place.
2026-09-24 21:21:11 -07:00
Matthew Jackson 1121522534 chore(release): bump to 1.7.6, follow the CI-required release-notes shape
Bumps Cargo.toml + Cargo.lock to 1.7.6. Also fixes a CI-shape defect the
previous release left behind: dev's CHANGELOG still said
`## [1.7.5] — UNRELEASED` after 1.7.5 shipped (release-orchestrate dated
main but did not push a fresh UNRELEASED heading back to dev). Corrects
that here so the changelog job accepts 1.7.6 on the way to qa:

  ## [1.7.6] — UNRELEASED
    - Version aligned; unified release driven by freemkv 1.7.6.
  ## [1.7.5] — 2026-09-23
    - Version aligned (or actual content, per the shipped main entry).

The driver for 1.7.6 is the freemkv Linux desktop shell (GTK4 +
libadwaita) + rip-finished desktop notification (issue #56).
2026-09-24 20:23:08 -07:00
Matthew Jackson 146f53873d v1.7.5: bump version (unified release) v1.7.5 v1.7.5-rc1 2026-09-23 04:40:56 +00:00
Matthew Jackson b925a4fa50 docs: 1.7.5 changelog entry for the unified release [skip-media]
Version aligned to 1.7.5. No functional change to this crate — the release
is driven by freemkv-unlock mirroring the freemkv-firmware 0.9.0 ABI, where
the drive's separate Ake (0x06) and Bus (0x07) levers are retired into a
single Encryption (0x06) lever.
v1.7.4-rc8
2026-09-22 21:05:01 -07:00
Matthew Jackson 4f169a4881 v1.7.4: bump version (unified release) v1.7.4 v1.7.4-rc7 2026-09-21 23:03:38 +00:00
Matthew Jackson e251e0bf6f docs: mark 1.7.4 changelog heading UNRELEASED (release workflow dates it) v1.7.4-rc6 2026-09-21 15:07:11 -07:00
Matthew Jackson 6704c1e05b docs: 1.7.4 changelog + consumer release notes v1.7.4-rc5 2026-09-21 11:47:20 -07:00
Matthew Jackson a34980970b docs/ci: drop stale local-precommit/release.sh references — CI is the only gate v1.7.4-rc4 2026-09-21 09:11:35 -07:00
Matthew Jackson 948511853e test(online): trim interpret_reply reachability aside to 3 lines for comment-guard v1.7.4-rc3 2026-09-21 08:51:04 -07:00
Matthew Jackson 373cdc4cf2 ci: adopt central freemkv/.github guards + checkout-siblings composite
Thin-caller comment/leak/identity guards → freemkv/.github reusable
workflows; drop local ci/comment-guard.py and ci/leak-guard.sh. Replace
per-job sibling checkout + .cargo/config heredoc with the shared
checkout-siblings@v1 composite (checkout libfreemkv+freemkv-unlock,
patch libfreemkv). Add cancel-in-progress concurrency to ci.yml/qa.yml;
standardize actions/checkout to @v5.
2026-09-21 08:48:58 -07:00
Matthew Jackson a60b021c35 Merge: ureq 3.4.2 + bounded reply body (supersedes Dependabot #12) 2026-09-20 20:15:19 -07:00
Matthew Jackson fc8b6e3a05 bump ureq 3.4.2 + preserve rolling-idle body timeout
ureq 3.4.1 (#1194, "Fix timeout budgets restarting and applying to later
phases") made timeout_recv_response bound only the response HEADERS and
turned timeout_recv_body into an ABSOLUTE total-body deadline. The online
key-service POST set recv_response only, so under 3.4.2 the reply body read
had NO deadline at all — a server that sent headers then stalled mid-reply
would hang the key request forever.

The /decode reply is a small JSON key answer that arrives in one read (a
few hundred bytes; the 1 MiB cap is a defensive anti-OOM ceiling, not an
expected size), so this is a fine total-cap case, not a large-body slow-link
download: no rolling-idle connector is warranted. Set timeout_recv_body to
the same TIMEOUT_SECS total ceiling so the POST can never hang forever.

Regression guard the_reply_body_read_is_bounded_not_only_the_headers asserts
recv_body is now set. Full suite + precommit (fmt/clippy/deny/tests) green.
2026-09-20 20:03:54 -07:00
Matthew Jackson bdbc805ba5 keydb: report match state + entry shape to de-conflate "no entry" (#46)
Implement KeySource::resolve_unit_keys for KeydbSource by splitting
unit_keys_from into resolve_from, which reports: whether find_disc
matched; the specific miss reason (NoVid when a Media Key has no VID,
else NoDerivableKey); the matched entry's shape (vuk / unit_keys count /
media_key / keydb_vid / enc_title_keys len / vid_available); and the
loaded entry count for a self-diagnosing true-miss verdict.

MultiSource forwards resolve_unit_keys so a matched inner source's richer
verdict survives composition. No derivation-logic change: the locked
VID-per-path rule is intentional and the investigation found no
correctness bug there — only the conflated verdict. Tests cover
matched-no-VID, matched-no-material, true miss, and the success path.
2026-09-20 19:16:06 -07:00
Matthew Jackson fa75d28441 freemkv-keysources 1.7.4: keydb loads every entry — drop entry cap, strip BOM, count malformed rows
- Remove the MAX_DISC_ENTRIES cap: a keydb is a database and must hold every
  disc row it contains (a count-based drop silently lost real discs).
- Strip a leading UTF-8 BOM before parsing so a Windows-saved keydb's FIRST
  disc row survives (U+FEFF is not trim()-able whitespace).
- Gate the disc branch on the 0x/0X prefix alone and COUNT a malformed 0x row
  (missing " = ") as rejected instead of silently ignoring it — surfaces the
  "hash is in the file but nothing resolves" class of bug.
- Tests: BOM, CRLF, leading whitespace, case/prefix-insensitive lookup,
  duplicate-hash last-wins, unit-key-only rows, 600k-entry no-drop, and the
  load_failure verdicts (permission / invalid / missing).

[skip-media]
v1.7.4-rc2
2026-09-20 09:43:41 -07:00