Mux: pure decrypt, policy at the caller (no null, no key-server storm)
decrypt_sectors is now a pure decrypt — apply the CPS unit key, leave the
plaintext, report how many bytes did not reach clean TS ("unverified"). It
never restores ciphertext, nulls, or re-fetches. "Did a key produce clean TS?"
is a key-selection / read-verify signal, not the verdict "did we decrypt?": a
correct key can decrypt a bad-encoded region, and broken TS is a muxer concern
(the demuxer drops the packet and resyncs).
Callers own the policy:
- mux (read > decrypt > mux): pass the decrypted bytes to the muxer, whatever
they are; fail loud only on a genuine can't-decrypt (no key / misaligned).
- sweep/patch (reading from a disc): an unverified unit is a bad read — recover
a fresh key and retry, or fail loud so disc-recovery re-reads it.
Removes three duplicated decisions — the decrypt-time ciphertext restore, the
mux NULL-TS conceal loop, and the per-unit key-server refetch — plus the dead
aacs_unit_still_ciphertext predicate. Key-fetch recovery now samples the on-disc
ciphertext explicitly (a pure decrypt leaves the buffer plaintext) and lives
only on the rip/verify path, never the mux.
Fixes the 30-90s/region mux stalls and key-server storm on bad-encoded UHD runs
that 1.4.1 left behind (it relaxed the gate but not the surrounding machinery).
This commit is contained in:
+5
-4
@@ -237,10 +237,11 @@ impl DiscStream {
|
||||
|
||||
// CSS/unencrypted content needs a decrypting wrapper to yield plaintext
|
||||
// VOB bytes before the AC-3 sub-stream probe can read real `acmod`s.
|
||||
// MUX path: tolerate decrypt loss — conceal an undecryptable unit (NULL TS
|
||||
// fill) + tally + log rather than abort the stream (P3). DiscStream is a
|
||||
// decode/mux stream (live-drive single-pass / direct), never the
|
||||
// ciphertext-preserving sweep, so concealment is always correct here.
|
||||
// MUX path (read > decrypt > mux): decrypt every unit in place and pass the
|
||||
// bytes to the muxer; a unit that decrypts to broken TS is the muxer's
|
||||
// concern, never conceal / re-fetch / count as loss (fail loud only on a
|
||||
// genuine can't-decrypt). DiscStream is a decode/mux stream (live-drive
|
||||
// single-pass / direct), never the ciphertext-preserving sweep.
|
||||
let mut reader =
|
||||
DecryptingSectorSource::new(reader, decrypt_keys.clone()).tolerate_decrypt_loss();
|
||||
|
||||
|
||||
+11
-17
@@ -645,26 +645,20 @@ pub fn build_iso_pipeline<S: SectorSource + Send + 'static>(
|
||||
crate::decrypt::DecryptKeys::Aacs { .. } => 3,
|
||||
_ => 1,
|
||||
};
|
||||
// MUX path: tolerate decrypt loss. An undecryptable content unit is concealed
|
||||
// (NULL TS fill) + tallied + logged, never an abort — decrypt-verify is a RIP
|
||||
// gate, not a mux gate (P3). The rip's own read paths keep their fail-loud
|
||||
// decorator; only this mux pipeline opts in.
|
||||
// MUX path: read > decrypt > mux. The decrypt seam applies the CPS unit key and
|
||||
// passes the bytes to the muxer; a unit that decrypts to broken TS is the
|
||||
// muxer's problem, not a decrypt failure, so the mux never conceals, re-fetches
|
||||
// a key, or counts it as loss — it fails only when it genuinely can't decrypt
|
||||
// (no key / misaligned unit). The `fetch` key-recovery seam is a rip/verify
|
||||
// concern (Disc::sweep / Disc::patch), deliberately NOT installed on the mux:
|
||||
// key recovery happens up front, and the mux never re-asks mid-stream.
|
||||
let mut decrypting =
|
||||
crate::sector::DecryptingSectorSource::new(Box::new(reader) as Box<dyn SectorSource>, keys)
|
||||
.tolerate_decrypt_loss();
|
||||
// Install the fresh-key-on-failure callback (if any) so a unit no held key
|
||||
// decrypts is re-tried via the application's key source before being counted
|
||||
// as loss. An AACS 2.1 forensic-segment unit that no key opens is just an
|
||||
// undecryptable unit like any other: concealed and counted as decrypt loss —
|
||||
// a loss is a loss, no FMTS special casing.
|
||||
if let Some(cb) = fetch {
|
||||
decrypting = decrypting.with_key_fetch(cb);
|
||||
}
|
||||
// Grab the loss counters before the decorator is moved into the producer
|
||||
// thread. It tracks bytes of scrambled AACS units no key could decrypt —
|
||||
// silent loss the demux drops; the consuming stream surfaces it through
|
||||
// `lost_bytes()` so the mux abort gate sees a partial decrypt failure rather
|
||||
// than a clean rip. Forensic (2.1) undecryptable units land here too.
|
||||
let _ = &fetch; // rip/verify key-recovery seam; the mux does not consume it
|
||||
// Loss counter: the mux does not tally broken-TS units (the muxer handles them),
|
||||
// so for a keyed disc this stays 0; it still surfaces via `lost_bytes()` for the
|
||||
// abort gate, which now reflects only a genuine can't-decrypt.
|
||||
let decrypt_loss = decrypting.decrypt_loss();
|
||||
|
||||
// Wrong-substream fix (Silence-of-the-Lambs): before the prefetcher takes
|
||||
|
||||
Reference in New Issue
Block a user