fix(css): no provable key is a hard failure, matching AACS

descramble_region descrambled with the key a sector's own crib had just
proven stale, whenever the re-crack from that sector also failed. The
clear header is not scrambled, so it survives intact: the sector still
opens with a valid pack start and passes every structural check the PS
demuxer applies. Only the payload is corrupted — exactly where nothing
looks. Ok(0) dropped, exit 0.

CSS has no external key source. The title key comes only from cracking
the data, so on a READABLE sector "no key" is not a missing input, it is
recovery failing on bytes we can see. That should never happen, and when
it does the answer is not to emit something.

Now Error::DecryptFailed — the same verdict the AACS path already gives
for a unit no held key opens. Both alternatives to failing are bad data
reported as success: descrambled with a rejected key it is garbage
behind a valid header, and passed through untouched it is ciphertext
where plaintext is meant to be.

WHY IT WAS POSSIBLE, which matters more than the fix:

There is no single place that owns "what do we do when there is no key".
decrypt_sectors_impl looks like the central dispatch, but its AACS arm
is a `return Err` stub — AACS decrypts entirely through
decrypt_sectors_mapped, a separate top-level path. So CSS decided its
own policy inside css/, AACS decided in decrypt.rs and mux/resolve.rs,
and nothing held them to the same answer. The asymmetry was not an
oversight; it was structurally permitted.

How a disc decrypts is one process — resolve a key for this data, apply
it, refuse if it cannot be proven. Only the resolve-and-apply step is
scheme-specific. Filed as a task: the policy belongs in one orchestrator
with the schemes supplying only what genuinely differs.

Two tests changed rather than added, both of which pinned the old
behaviour: the unit test asserted the sector was descrambled, and the
integration test asserted the scramble flag was cleared, which is what
descrambling-with-any-key does. Neither established that the result was
CORRECT — the fourth bad-test shape.
This commit is contained in:
Matthew Jackson
2026-07-30 19:10:32 -07:00
parent b2b611fa3b
commit 30bea12392
3 changed files with 119 additions and 16 deletions
+1 -2
View File
@@ -567,8 +567,7 @@ fn decrypt_sectors_impl(
// `css::descramble_region`), and CSS does not need the post-decrypt
// recovery seam that AACS key-fetch / FMTS segment-skip use (those DO
// consume external inputs a `decrypt_sectors` caller cannot supply).
css::descramble_region(buf, title_key);
0
css::descramble_region(buf, title_key)?
}
};
Ok(dropped)