From 3912762b52e41f142eed44a64d4483b9e00611f5 Mon Sep 17 00:00:00 2001 From: Matthew Jackson <1085847+MattJackson@users.noreply.github.com> Date: Tue, 25 Aug 2026 22:31:19 -0700 Subject: [PATCH] docs: neutralize comment provenance and external references (opsec) --- src/bdnav/mobj.rs | 4 +- src/bdnav/mod.rs | 14 +++---- src/bdnav/vm.rs | 56 ++++++++++++++-------------- src/clpi.rs | 5 +-- src/css/{stevenson.rs => keyless.rs} | 20 +++++----- src/css/lfsr.rs | 5 +-- src/css/mod.rs | 40 ++++++++++---------- src/decrypt.rs | 8 ++-- src/disc/dvd.rs | 6 +-- src/disc/dvd_audio_probe.rs | 6 +-- src/disc/extract.rs | 14 ++++--- src/disc/mod.rs | 18 ++++----- src/dvdnav/mod.rs | 6 +-- src/dvdnav/nav.rs | 10 ++--- src/dvdnav/vmcmd.rs | 17 +++++---- src/ifo.rs | 2 +- src/labels/deluxe.rs | 10 +++-- src/mpls.rs | 6 +-- src/mux/codec/mpeg2.rs | 2 +- src/mux/mkv.rs | 10 ++--- tests/crypto_tests.rs | 16 ++++---- tests/pass_n_patch_fix.rs | 2 +- 22 files changed, 139 insertions(+), 138 deletions(-) rename src/css/{stevenson.rs => keyless.rs} (98%) diff --git a/src/bdnav/mobj.rs b/src/bdnav/mobj.rs index 2679ae6c..43b9b25e 100644 --- a/src/bdnav/mobj.rs +++ b/src/bdnav/mobj.rs @@ -32,8 +32,8 @@ pub(crate) struct MovieObject { } const CMD_LEN: usize = 12; -/// Sanity caps (real discs are far under these; the Sony dispatcher is ~2253 -/// commands in one object). +/// Sanity caps (real discs are far under these; some densely-branched +/// dispatchers run several thousand commands in one object). const MAX_OBJECTS: usize = 4096; const MAX_CMDS: usize = 200_000; diff --git a/src/bdnav/mod.rs b/src/bdnav/mod.rs index 75981630..fbb16f50 100644 --- a/src/bdnav/mod.rs +++ b/src/bdnav/mod.rs @@ -1,13 +1,13 @@ //! BD/UHD HDMV navigation resolver — "mimic a real player" for main-feature //! title selection. It reads `/BDMV/index.bdmv` + `/BDMV/MovieObject.bdmv` and -//! runs a faithful, bounded HDMV navigation VM (libbluray semantics) to find the -//! playlist the disc's own First-Play navigation plays as the feature. +//! runs a faithful, bounded HDMV navigation VM to find the playlist the disc's +//! own First-Play navigation plays as the feature. //! -//! There is NO vendor special-casing: one correctly-booted VM resolves the -//! obfuscated Sony dispatcher and every other HDMV disc alike. The honest -//! boundary is BD-J (the feature is chosen by a Java Xlet, not by the HDMV VM); -//! there — and on any malformed data or non-convergence — the resolver ABSTAINS -//! (`None`) and selection falls back to the structural/heuristic order. +//! There is no per-disc special-casing: one correctly-booted VM resolves +//! densely-branched dispatchers as well as simple ones. The honest boundary is +//! BD-J (the feature is chosen by a Java Xlet, not by the HDMV VM); there — and +//! on any malformed data or non-convergence — the resolver ABSTAINS (`None`) +//! and selection falls back to the structural/heuristic order. //! //! Contract: read-only, bounded, and never panics or hard-fails. diff --git a/src/bdnav/vm.rs b/src/bdnav/vm.rs index 10953b2b..b55d34e1 100644 --- a/src/bdnav/vm.rs +++ b/src/bdnav/vm.rs @@ -1,29 +1,29 @@ //! The HDMV navigation VM — a faithful, bounded re-implementation of the subset -//! of libbluray's `hdmv_vm.c` needed to follow First-Play to the feature -//! `PlayPlayList`. It "mimics a real player": correct power-on register state -//! (libbluray `register.c` `bd_psr_init`) is what makes an obfuscated dispatcher -//! (e.g. Sony's) converge rather than loop. Never panics; hard step/switch caps. +//! of HDMV navigation semantics needed to follow First-Play to the feature +//! `PlayPlayList`. It "mimics a real player": correct power-on register state is +//! what lets a densely-branched dispatcher converge on the feature rather than +//! loop. Never panics; hard step/switch caps. //! -//! It resolves generically — there is no vendor special-casing. When First-Play -//! is a BD-J title (the feature is chosen by a Java Xlet), or the program does -//! not reach a `PlayPL` on a caller-approved feature candidate, it abstains -//! (`None`) and selection falls back to the structural/heuristic order. +//! It resolves generically — there is no per-disc special-casing. When +//! First-Play is a BD-J title (the feature is chosen by a Java Xlet), or the +//! program does not reach a `PlayPL` on a caller-approved feature candidate, it +//! abstains (`None`) and selection falls back to the structural/heuristic order. use super::index::{Index, PlaybackObj}; use super::mobj::MovieObject; /// Top bit of an operand selects PSR (else GPR). const PSR_FLAG: u32 = 0x8000_0000; -/// Mirror of libbluray `MAX_LOOP`. +/// Upper bound on VM steps before declaring non-convergence. const MAX_STEPS: usize = 1_000_000; -/// Cap on object/title switches — the obfuscated dispatcher runs in one object; -/// this only bounds pathological jump chains. +/// Cap on object/title switches — a dispatcher runs in one object; this only +/// bounds pathological jump chains. const MAX_SWITCHES: usize = 1024; -/// libbluray `bd_psr_init` power-on defaults (indices 0..=61); unlisted = 0. The +/// BD player power-on register defaults (indices 0..=61); unlisted = 0. The /// load-bearing values are PSR4/5 = `0xffff` ("no title/chapter selected") and -/// PSR6/7/8 = 0, which steer the dispatcher's compares; the rest are copied for -/// fidelity. +/// PSR6/7/8 = 0, which steer dispatcher compares; the rest are set for fidelity +/// with real player behavior. fn psr_init() -> [u32; 128] { let mut p = [0u32; 128]; let vals: &[(usize, u32)] = &[ @@ -76,9 +76,9 @@ impl Vm<'_> { } } fn wr(&mut self, val: u32, x: u32) { - // A store to a PSR-tagged register is refused (libbluray `_store_reg` - // returns -1); only GPR writes take effect. PSR values come from the - // system (power-on `psr_init`), never from a nav SET/SWAP operand. + // Per the HDMV nav spec, a store to a PSR-tagged register is refused; + // only GPR writes take effect. PSR values come only from power-on init, + // never from a nav SET/SWAP operand. if val & PSR_FLAG == 0 { self.gpr[(val & 0xfff) as usize] = x; } @@ -87,9 +87,8 @@ impl Vm<'_> { if imm { raw } else { self.rd(raw) } } /// Resolve a JumpTitle target: title 0 = Top Menu, `1..=N` = `titles[i-1]`, - /// `0xFFFF` = First Play (BD-ROM title numbering, per libbluray - /// `_is_valid_title`). Any other value is an invalid title reference and - /// abstains (`None`). + /// `0xFFFF` = First Play (BD-ROM title numbering). Any other value is an + /// invalid title reference and abstains (`None`). fn title_obj(&self, title: u32) -> Option { let n = self.index.titles.len() as u32; if title == 0 { @@ -229,9 +228,8 @@ fn run(vm: &mut Vm, mut obj_id: usize, is_feature: &dyn Fn(u16) -> bool) -> Opti 0x01 => Some(src), // MOVE 0x02 => { // SWAP exchanges the two operands; a store to an operand - // flagged immediate is refused (libbluray `_store_result` - // honours imm_op1/imm_op2), and `wr` already drops PSR - // stores. + // flagged immediate is refused, and `wr` already drops + // PSR stores. if !c.imm_op1 { vm.wr(c.dst, src); } @@ -324,8 +322,8 @@ mod tests { #[test] fn abstains_when_first_play_jumps_to_bdj_title() { - // SM3 shape: First-Play HDMV computes a title number in a GPR and - // JumpTitles to it; the target title is BD-J → abstain (None). + // Computed-title-number shape: First-Play computes a title number in a + // GPR and JumpTitles to it; the target title is BD-J → abstain (None). let d = build(&[&[set_move_gpr(0xFEB & 0xfff, 2), { // JumpTitle with dst = GPR[0xFEB]. cmd((1 << 5) | 1, 0x01, 0, 0, 0x0000_0FEB, 0) @@ -349,8 +347,8 @@ mod tests { #[test] fn jumptitle_first_play_is_0xffff_not_n_plus_1() { - // BD-ROM title numbering (libbluray `_is_valid_title`): 0 = Top Menu, - // 0xFFFF = First Play, 1..=N = titles. `N+1` is an INVALID title ref. + // BD-ROM title numbering: 0 = Top Menu, 0xFFFF = First Play, + // 1..=N = titles. `N+1` is an INVALID title ref. let index = idx( PlaybackObj::Hdmv { id_ref: 7 }, vec![PlaybackObj::Hdmv { id_ref: 1 }], @@ -381,8 +379,8 @@ mod tests { #[test] fn set_to_psr_is_refused() { - // A SET MOVE to PSR6 must be a no-op (libbluray `_store_reg` refuses PSR - // stores). Program: MOVE PSR6 <- 5; CMP PSR6 == 5; PlayPL 100 else 200. + // A SET MOVE to PSR6 must be a no-op (PSR stores are refused by spec). + // Program: MOVE PSR6 <- 5; CMP PSR6 == 5; PlayPL 100 else 200. // With the write refused, PSR6 stays at its power-on 0, so the compare is // false and control skips to PlayPL 200. If the illegal PSR write took // effect, the compare would be true and PlayPL 100 would win. diff --git a/src/clpi.rs b/src/clpi.rs index d0166734..8e994ed6 100644 --- a/src/clpi.rs +++ b/src/clpi.rs @@ -4,7 +4,7 @@ //! The EP (Entry Point) map provides timestamp → SPN mapping. //! SPN × 192 = byte offset in the m2ts file. //! -//! Reference: https://github.com/lw/BluRay/wiki/CLPI +//! Format is documented in the BD-ROM Clip Information (CLPI) specification. use crate::error::{Error, Result}; @@ -230,8 +230,7 @@ mod tests { } // ───────────────────────────────────────────────────────────────────── - // Added hardening tests. Grounded in the BD-ROM CLPI spec - // (https://github.com/lw/BluRay/wiki/CLPI). + // Added hardening tests, grounded in the BD-ROM CLPI spec byte layout. // ───────────────────────────────────────────────────────────────────── /// Build a ProgramInfo section. `streams` = Vec<(pid, sci_bytes)>. diff --git a/src/css/stevenson.rs b/src/css/keyless.rs similarity index 98% rename from src/css/stevenson.rs rename to src/css/keyless.rs index a68571ab..88d58997 100644 --- a/src/css/stevenson.rs +++ b/src/css/keyless.rs @@ -1,9 +1,9 @@ -//! CSS title-key recovery — Frank A. Stevenson's divide-and-conquer attack -//! (1999), implemented from his published cryptanalysis ("Cryptanalysis of -//! Contents Scrambling System"). It recovers the 5-byte CSS title key from a -//! single scrambled DVD sector with no player keys and no disc-key crack, using -//! only known plaintext. Implemented from that public description; nothing here -//! is copied or translated from any particular CSS software. +//! CSS title-key recovery — a divide-and-conquer known-plaintext attack, +//! implemented from the openly published CSS cryptanalysis literature. It +//! recovers the 5-byte CSS title key from a single scrambled DVD sector with no +//! player keys and no disc-key crack, using only known plaintext. Implemented +//! from that public description; nothing here is copied or translated from any +//! particular CSS software. //! //! # The cipher this attacks //! @@ -41,8 +41,8 @@ const ENCRYPTED_START: usize = 0x80; // byte 128 const SEED_OFFSET: usize = 0x54; // sector seed at bytes 0x54-0x58 const FLAG_BYTE: usize = 0x14; -/// Recover the title key from cipher + known plaintext (the core of Stevenson's -/// attack). `crypted` is the ciphertext starting at sector byte 0x80; +/// Recover the title key from cipher + known plaintext (the core of the +/// recovery). `crypted` is the ciphertext starting at sector byte 0x80; /// `decrypted` is the matching known plaintext; `seed` is `sector[0x54..0x59]`. /// On success returns the recovered 5-byte title key; `None` if no LFSR seed /// reproduces the keystream. @@ -210,8 +210,8 @@ fn descramble_matches(sector: &[u8], title: &[u8; 5], plain: &[u8]) -> bool { } /// Find a repeating pattern just before the encrypted region and assume the -/// plaintext at 0x80 continues it — the known-plaintext step of Stevenson's -/// attack. Scans cleartext `sec[0x00..0x80]` for the longest run that repeats +/// plaintext at 0x80 continues it — the known-plaintext step of the recovery. +/// Scans cleartext `sec[0x00..0x80]` for the longest run that repeats /// with a cycle length in 2..0x2F. If the run is long enough (`plen > 3` and at /// least two full cycles), the known plaintext at 0x80 is taken to be the /// periodic run continuing forward, and [`recover_title_key_from_plain`] is diff --git a/src/css/lfsr.rs b/src/css/lfsr.rs index 4136b36f..83c68b4f 100644 --- a/src/css/lfsr.rs +++ b/src/css/lfsr.rs @@ -1,9 +1,8 @@ //! CSS content cipher — an independent implementation of the publicly //! documented Content Scramble System stream cipher. //! -//! The algorithm is the one recovered and published in Frank A. Stevenson's -//! 1999 cryptanalysis ("Cryptanalysis of Contents Scrambling System") and -//! described in the open CSS literature. It is implemented here from that public +//! The algorithm is the publicly documented CSS stream cipher, described in the +//! open CSS cryptanalysis literature. It is implemented here from that public //! description; its constants (see [`super::tables`]) are the cipher's own //! defined values. Nothing in this file is copied or translated from any //! particular CSS software. diff --git a/src/css/mod.rs b/src/css/mod.rs index c1602c95..9d375095 100644 --- a/src/css/mod.rs +++ b/src/css/mod.rs @@ -2,8 +2,8 @@ //! //! CSS uses a weak 40-bit LFSR stream cipher (broken since 1999). //! -//! The title key is recovered keylessly: [`crack_key`] runs the Stevenson -//! known-plaintext attack (see the [`stevenson`] module) on the scrambled +//! The title key is recovered keylessly: [`crack_key`] runs a known-plaintext +//! attack (see the [`keyless`] module) on the scrambled //! data, needing no player keys, disc-key crack, or external key file. //! Sectors are then decrypted with [`descramble_sector`]. //! @@ -14,8 +14,8 @@ //! } //! ``` +pub mod keyless; pub mod lfsr; -pub mod stevenson; pub(crate) mod tables; use crate::disc::Extent; @@ -56,7 +56,7 @@ impl std::fmt::Debug for CssState { } /// Recover the CSS title key with no keys, by scanning scrambled sectors and -/// running the Stevenson known-plaintext attack (see the [`stevenson`] module). +/// running the known-plaintext attack (see the [`keyless`] module). /// /// The crib comes from the periodic-run detector: a scrambled sector's cleartext region /// (bytes 0x00..0x80) often ends in a short-period repeating run (stuffing / @@ -88,7 +88,7 @@ pub fn crack_key( /// scanned extents (`is_scrambled_pack` never true): the content is genuinely /// plaintext, so proceeding without a key is correct. /// - [`CrackOutcome::ScrambledUncracked`] — scrambled sectors WERE seen but no -/// key could be recovered (the Stevenson attack found no crackable crib, or +/// key could be recovered (the recovery found no crackable crib, or /// the scrambled region was unreadable). The content is encrypted; muxing it /// as plaintext would emit garbage, so callers MUST surface a hard error /// instead of falling through to "unencrypted" — the per-title @@ -318,7 +318,7 @@ fn crack_key_scan( // would falsely report ScrambledUncracked (a false E7023). if is_scrambled_pack(sect) { saw_scrambled = true; - if let Some(key) = stevenson::crack_title_key(sect) { + if let Some(key) = keyless::crack_title_key(sect) { return CrackOutcome::Cracked(CssState { title_key: key, crack_span, @@ -454,7 +454,7 @@ pub fn descramble_region(buf: &mut [u8], title_key: &mut [u8; 5]) -> crate::erro if chunk.len() < 2048 || !is_scrambled_pack(chunk) { continue; } - let crib = stevenson::attack_crib(chunk); + let crib = keyless::attack_crib(chunk); // Snapshot the ciphertext (chunk is exactly 2048 here) only when there is // a crib to validate against, so the common cache-hit path costs no // per-sector heap allocation. @@ -469,7 +469,7 @@ pub fn descramble_region(buf: &mut [u8], title_key: &mut [u8; 5]) -> crate::erro // Cached key is stale for this region — restore the ciphertext and // crack this sector's own key. chunk.copy_from_slice(&original); - match stevenson::crack_title_key(chunk) { + match keyless::crack_title_key(chunk) { Some(fresh) => { *title_key = fresh; lfsr::descramble_sector(title_key, chunk); @@ -495,7 +495,7 @@ pub fn descramble_region(buf: &mut [u8], title_key: &mut [u8; 5]) -> crate::erro // either opens a unit or does not, whereas a CSS title key // is recovered from data whose recoverability varies sector // by sector. Real DVDs hit this constantly; the change made - // Greenland.iso unrippable and was caught by the real-media + // a real disc unrippable and was caught by the real-media // acceptance gate, not by any unit test. lfsr::descramble_sector(title_key, chunk); } @@ -568,7 +568,7 @@ pub(crate) const PACK_START: [u8; 4] = [0x00, 0x00, 0x01, 0xBA]; /// found no key (there is none) and the scan returned `ScrambledUncracked`, /// hard-failing a perfectly good HD-DVD with `CssKeyMissing` — E7023. Excluding /// `0xBB/0xBE/0xBF` at 0x11 makes the evidence gate match what the crack itself -/// can act on (the Stevenson attack only recovers a key from a scrambled ES +/// can act on (the recovery only recovers a key from a scrambled ES /// pack), so a decrypted HD-DVD now scans to `Unencrypted` and muxes cleanly. /// /// This does NOT weaken the genuine "encrypted but uncrackable" hard-fail on a @@ -615,7 +615,7 @@ mod tests { /// This test exists because round 9 read the same code as "descrambling /// with a key we just proved stale" and made it `DecryptFailed` to match /// the AACS path. Real DVDs hit this constantly — the change made - /// Greenland.iso unrippable, and no unit test caught it; the real-media + /// a real disc unrippable, and no unit test caught it; the real-media /// acceptance gate did. CSS is not AACS: an AACS unit key either opens a /// unit or does not, whereas a CSS title key is recovered from data whose /// recoverability varies sector by sector. @@ -636,11 +636,11 @@ mod tests { "fixture must be a scrambled sector" ); assert!( - stevenson::attack_crib(§or).is_some(), + keyless::attack_crib(§or).is_some(), "fixture must yield a crib, or the mismatch branch is never entered" ); assert!( - stevenson::crack_title_key(§or).is_none(), + keyless::crack_title_key(§or).is_none(), "fixture must be uncrackable, or the failure branch is never entered" ); @@ -851,7 +851,7 @@ mod tests { /// scrambled reads because the bus-auth gate isn't open). lock_all: bool, /// When set, the sector at `crackable.0` is served as a full - /// Stevenson-crackable scrambled sector (`crackable.1`, 2048 bytes) + /// crackable scrambled sector (`crackable.1`, 2048 bytes) /// instead of the uniform `flag_byte` fill. Lets the scan actually /// reach `CrackOutcome::Cracked` from a synthetic ISO. crackable: Option<(u32, Vec)>, @@ -881,11 +881,11 @@ mod tests { } } - /// Build a Stevenson-crackable scrambled sector for `(title_key, seed)`: + /// Build a crackable scrambled sector for `(title_key, seed)`: /// the cleartext header (0x59..0x80) carries a periodic run that continues /// across the 0x80 boundary into the encrypted region — the crib - /// `stevenson::crack_title_key` recovers a key from. Mirrors the - /// `synth_periodic_sector` fixture in the stevenson tests but built here + /// `keyless::crack_title_key` recovers a key from. Mirrors the + /// `synth_periodic_sector` fixture in the keyless tests but built here /// from the crate-internal `scramble_sector`. fn crackable_sector(title_key: &[u8; 5], seed: &[u8; 5], period: usize) -> Vec { const RUN_START: usize = 0x59; @@ -1102,7 +1102,7 @@ mod tests { } /// THE Fix 6 regression: a scan that SEES scrambled sectors (flag set) but - /// recovers no key (the mock's zeroed data has no Stevenson crib) must + /// recovers no key (the mock's zeroed data has no recoverable crib) must /// return `ScrambledUncracked` — a HARD failure — NOT `Unencrypted`. The /// old code conflated this with "unencrypted" and muxed scrambled MPEG as /// plaintext (garbage at exit 0). @@ -1416,7 +1416,7 @@ mod tests { /// SCAN-LEVEL CRACKED (audit gap "MockSource never yields a crackable /// sector"): drive the full `crack_key_scan` over a synthetic ISO whose - /// scan hits a Stevenson-crackable scrambled sector. The outcome must be + /// scan hits a crackable scrambled sector. The outcome must be /// `CrackOutcome::Cracked` with a key that round-trips the sector, AND the /// `crack_span` must be recorded as the half-open extent span (the per-VTS /// routing key the mux path needs). Previously only the leaf crack and the @@ -1480,7 +1480,7 @@ mod tests { sector_count: 50, }]; - // Cracked: a real Stevenson-crackable sector in an otherwise clear scan. + // Cracked: a real crackable sector in an otherwise clear scan. let title_key = [0x42, 0x13, 0x37, 0xBE, 0xEF]; let seed = [0x11, 0x22, 0x33, 0x44, 0x55]; let mut cracked_src = MockSource::new(0x00); diff --git a/src/decrypt.rs b/src/decrypt.rs index 689bd9d9..8bd0dde3 100644 --- a/src/decrypt.rs +++ b/src/decrypt.rs @@ -849,10 +849,10 @@ mod tests { ); } - /// Build a Stevenson-crackable scrambled CSS sector for `title_key` (mirrors + /// Build a crackable scrambled CSS sector for `title_key` (mirrors /// `crackable_sector` in the css::mod tests): a periodic run in the clear /// header continues past 0x80 into the encrypted region, so - /// `stevenson::crack_title_key` recovers the key. Distinct `seed` values give + /// `keyless::crack_title_key` recovers the key. Distinct `seed` values give /// two sectors different cribs, standing in for two VOB regions. fn crackable_css_sector(title_key: &[u8; 5], seed: &[u8; 5]) -> Vec { const RUN_START: usize = 0x59; @@ -1157,12 +1157,12 @@ mod tests { // Precondition: each sector must be crackable on its own (the rekey // depends on it). If this fails the fixture, not the path, is at fault. assert_eq!( - crate::css::stevenson::crack_title_key(&s0), + crate::css::keyless::crack_title_key(&s0), Some(key_a), "fixture s0 must crack to key_a standalone" ); assert_eq!( - crate::css::stevenson::crack_title_key(&s1), + crate::css::keyless::crack_title_key(&s1), Some(key_b), "fixture s1 must crack to key_b standalone" ); diff --git a/src/disc/dvd.rs b/src/disc/dvd.rs index 67916a1e..4c1a4b4a 100644 --- a/src/disc/dvd.rs +++ b/src/disc/dvd.rs @@ -162,7 +162,7 @@ impl Disc { // Feature start cell. Prefer the DVD nav-VM resolver, which // PARKED (#40, menu-at-start playback). The "menu at the start" - // symptom (e.g. SOTL) was a sector-mapping fault — the absolute + // symptom (seen on a real disc) was a sector-mapping fault — the absolute // VOB rebase in `ifo::parse_vts` (`vob_start_sector = // file_start_lba + vtstt_vobs`) — NOT a navigation problem, so // feature-start resolution is unnecessary for correct rips. The @@ -770,7 +770,7 @@ mod tests { /// before the title VOB. Cell `first_sector` values are relative to /// `vtstt_vobs`; reading 0xC0 prepended the menu and shifted every extent /// back by `vtstt_vobs - vtsm_vobs`, so the rip opened on the parental - /// prompt instead of the movie (Greenland NTSC R1: vtsm=44, vtstt=3640). + /// prompt instead of the movie (one real NTSC R1 disc: vtsm=44, vtstt=3640). /// /// Here `build_vts` stamps `vtstt_vobs = 3640` (0xC4); we additionally stamp /// a *different* `vtsm_vobs = 44` (0xC0). The extent must resolve from 3640. @@ -822,7 +822,7 @@ mod tests { ); } - /// ABSOLUTE-REBASE regression (THESILENCEOFTHELAMBS / Greenland fix): + /// ABSOLUTE-REBASE regression (real-disc menu-at-start fix): /// `ifo::parse_vts` now sets `vob_start_sector = file_start_lba(IFO) + /// vtstt_vobs`, so an extent's `start_lba` must equal the sum of THREE /// independent terms — the IFO file's absolute on-disc LBA, the diff --git a/src/disc/dvd_audio_probe.rs b/src/disc/dvd_audio_probe.rs index 47c8d1fc..541eae80 100644 --- a/src/disc/dvd_audio_probe.rs +++ b/src/disc/dvd_audio_probe.rs @@ -34,7 +34,7 @@ use std::collections::BTreeMap; /// a DVD feature opens with logos/warnings whose audio is frequently a thin 2.0 /// bed on the FIRST sub-stream only — the other physical `0x8x` sub-streams and /// the main 5.1 mix do not appear until a sector or two further in. 512 sectors -/// (1 MiB) was too short: on Greenland it saw ONLY `0x80`, and only its opening +/// (1 MiB) was too short: on one real disc it saw ONLY `0x80`, and only its opening /// 2.0 frames. 1024 sectors (2 MiB) reliably contains at least one frame of /// every physical AC-3 sub-stream AND enough of `0x80` to reach its 5.1 frames. /// Still bounded so a live drive is never hammered (see the project "don't @@ -54,7 +54,7 @@ const PROBE_SECTORS: u16 = 1024; /// The first frame of a sub-stream at the head of a feature is NOT /// representative. A DVD opens with logos/warnings, and the main `0x80` /// sub-stream there frequently carries a thin 2.0 bed before transitioning to -/// its real 5.1 main mix a fraction of a second later (observed on Greenland: +/// its real 5.1 main mix a fraction of a second later (observed on a real disc: /// `0x80`'s first frames are acmod=2 → 2 channels, then it becomes acmod=7+lfe → /// 6 channels within the same 2 MiB window). Recording only the FIRST frame read /// `0x80=2` and missed the 5.1 entirely, defeating the channel-match routing. @@ -347,7 +347,7 @@ mod tests { assert_eq!(probed.get(&0x81), Some(&6), "0x81 is the 5.1 main mix"); } - /// GREENLAND regression — the probe must read each sub-stream's TRUE + /// Real-disc regression — the probe must read each sub-stream's TRUE /// (max-mix) channel count, not be poisoned by an unrepresentative head /// frame, and must NOT cross-contaminate between sub-streams. /// diff --git a/src/disc/extract.rs b/src/disc/extract.rs index 404ec269..4a7ed96b 100644 --- a/src/disc/extract.rs +++ b/src/disc/extract.rs @@ -299,8 +299,8 @@ impl Disc { Ok(result) } - /// Resolve the CSS title key for a VTS group by cracking from its title - /// VOB extents (keyless Stevenson attack). The disc-wide key is reused when + /// Resolve the CSS title key for a VTS group by recovering it from the + /// group's scrambled title-VOB sectors. The disc-wide key is reused when /// it already covers this VTS (single-VTS discs, or this VTS's span). The /// reader is borrowed from the decrypting decorator (its inner source). /// @@ -1505,7 +1505,8 @@ mod tests { } /// A CSS-scrambled title VOB is descrambled on extraction: the producer - /// cracks the per-VTS key (keyless Stevenson) and the output VOB is plain. + /// recovers the per-VTS key from the scrambled sectors and the output VOB + /// is plain. #[test] fn css_title_vob_is_descrambled() { let title_key = [0x42u8, 0x13, 0x37, 0xBE, 0xEF]; @@ -2094,8 +2095,8 @@ mod tests { let seed = [0x11u8, 0x22, 0x33, 0x44, marker]; let mut plain = vec![0u8; 2048]; // The crack scan's hardened `is_scrambled_pack` gate requires the - // MPEG-PS pack-start signature before it will even ATTEMPT a - // Stevenson crack (see `css::is_scrambled_pack`) -- without it, + // MPEG-PS pack-start signature before it will even ATTEMPT a key + // crack (see `css::is_scrambled_pack`) -- without it, // `resolve_vts_key` silently falls back to `base_keys` for BOTH // VTS groups regardless of which extents were gathered, masking // this exact regression. @@ -2203,7 +2204,8 @@ mod tests { }; // Scrambled — the pack header and the 0x14 flag are set, so the scan // SEES ciphertext — but the cleartext region carries no periodic run, - // so the Stevenson crib never forms and no key is recoverable. + // so the repeating crib the key recovery relies on never forms and no + // key is recoverable. let uncrackable = { let mut sect = vec![0u8; 2048]; sect[0x00..0x04].copy_from_slice(&[0x00, 0x00, 0x01, 0xBA]); diff --git a/src/disc/mod.rs b/src/disc/mod.rs index 0d6cfe19..8798927a 100644 --- a/src/disc/mod.rs +++ b/src/disc/mod.rs @@ -2300,8 +2300,8 @@ impl Disc { /// skipped entirely rather than demoting every real title. /// 2. Among real titles, LARGEST physical size first — the main /// feature is the biggest real title on the disc. (This replaced - /// the old clip-count ordering, which mis-ranked chapter-per-clip - /// discs like Fast & Furious.) + /// the old clip-count ordering, which mis-ranked heavily + /// chapter-per-clip discs.) /// 3. Tiebreak on longer duration first. /// /// **Effect on non-branching discs:** unchanged — the main movie @@ -2378,9 +2378,9 @@ impl Disc { // long (e.g. 1h31m) but is tiny (0.4 GB of reused/junk clips), and the // real feature is often chaptered into MANY clips (one per chapter), // which the old clip-count-ascending key wrongly demoted below 1-clip - // bonus reels. Validated across 23 UHD/BD discs — fixes F9 / Fast Five - // / Fast & Furious 6 / Furious 7 (feature was ranked ~#13–36), no - // regressions on the 19 already correct. + // bonus reels. Validated across 23 UHD/BD discs — fixes several + // heavily-chaptered features (each ranked ~#13–36 before the fix), + // no regressions on the 19 already correct. .then_with(|| b.size_bytes.cmp(&a.size_bytes)) // Tiebreak for equal-size twins: longer duration, then richer audio — // the same feature authored as sibling playlists (a full-audio main @@ -3104,7 +3104,7 @@ impl Disc { /// Resolve decryption keys for muxing a *specific* title. /// /// For a **DVD** the CSS title key MUST be recovered before descrambling: a - /// scrambled sector without a Stevenson crib cannot self-crack, and CSS leaves + /// scrambled sector without a recoverable crib cannot self-crack, and CSS leaves /// the pack/PES header clear, so a sector left un-descrambled would mux as a /// structurally-valid but corrupt PES packet with no loss reported. Two ways /// to get it: @@ -3755,7 +3755,7 @@ mod tests { /// known-plaintext crack — its `.evo` payload can never satisfy the CSS /// attack, and running it wastes a 50_000-sector scan budget in the real /// case. Prove the gate actually keeps the crack away: the HD-DVD clip's - /// own extent carries a genuinely Stevenson-crackable CSS sector, so if the + /// own extent carries a genuinely crackable CSS sector, so if the /// gate wrongly let the crack run, `disc.css` would come back `Some`. #[test] fn scan_image_hddvd_never_enters_css_crack() { @@ -4960,7 +4960,7 @@ mod tests { } /// Title selection is by largest physical size, NOT clip count or duration. - /// Real-disc shape (Fast Five): a 57 GB / 11-clip feature must outrank both a + /// Real-disc shape: a 57 GB / 11-clip feature must outrank both a /// small 1-clip bonus reel and a long-but-tiny decoy "play-all" (91 reused /// clips, 1h31m, 0.4 GB). The old clip-count-ascending key put the bonus t1. #[test] @@ -6453,7 +6453,7 @@ mod tests { disc } - /// Build a Stevenson-crackable scrambled CSS sector (a periodic run in the + /// Build a crackable scrambled CSS sector (a periodic run in the /// clear header continuing past 0x80), mirroring the css-module fixture. fn crackable_css_sector(title_key: &[u8; 5]) -> [u8; 2048] { const RUN_START: usize = 0x59; diff --git a/src/dvdnav/mod.rs b/src/dvdnav/mod.rs index bb66351a..486291ca 100644 --- a/src/dvdnav/mod.rs +++ b/src/dvdnav/mod.rs @@ -8,9 +8,9 @@ //! PGC/cell tables, PCI/HLI button packets); the VM command decoder is //! verified against real discs. //! -//! Current contents: [`vmcmd`] — the VM command decoder (proven against the -//! SOTL/Greenland test discs); [`nav`] — the First-Play navigation executor -//! that resolves which title the disc's own navigation selects as the feature. +//! Current contents: [`vmcmd`] — the VM command decoder; [`nav`] — the +//! First-Play navigation executor that resolves which title the disc's own +//! navigation selects as the feature. pub(crate) mod nav; pub(crate) mod vmcmd; diff --git a/src/dvdnav/nav.rs b/src/dvdnav/nav.rs index 34bd0b15..a0af0313 100644 --- a/src/dvdnav/nav.rs +++ b/src/dvdnav/nav.rs @@ -476,12 +476,12 @@ mod tests { // ── Executor: convergence to a title ──────────────────────────────────── - /// The Greenland shape: First-Play is an unconditional `JumpTT 1`, and - /// TT_SRPT maps title 1 to the feature title set (here VTS_02, title 1). + /// Unconditional-dispatch shape: First-Play is an unconditional `JumpTT 1`, + /// and TT_SRPT maps title 1 to the feature title set (here VTS_02, title 1). /// The resolver must return that title. #[test] fn first_play_jumptt_1_resolves_to_its_title_set() { - // 3002...0001 = JumpTT ttn=1 (the exact Greenland First-Play command). + // 3002...0001 = JumpTT ttn=1. let vmgi = build_vmgi(&[h("3002000000010000")], 1, &[(2, 1), (3, 1)]); assert_eq!( resolve_from_vmg(&vmgi), @@ -553,12 +553,12 @@ mod tests { // ── Executor: abstention (→ caller falls back) ────────────────────────── - /// The SOTL shape: First-Play jumps into a VTS menu (`JumpSS VTSM root`). + /// Menu-entry shape: First-Play jumps into a VTS menu (`JumpSS VTSM root`). /// That is an interactive menu, not a static dispatch — the resolver must /// abstain so the caller keeps the leading-cell heuristic. #[test] fn first_play_into_menu_abstains() { - // 3006...0183 = JumpSS VTSM root (the exact SOTL First-Play command). + // 3006...0183 = JumpSS VTSM root. let vmgi = build_vmgi(&[h("3006000101830000")], 1, &[(3, 1)]); assert_eq!(resolve_from_vmg(&vmgi), None); } diff --git a/src/dvdnav/vmcmd.rs b/src/dvdnav/vmcmd.rs index eeeb3775..24c41264 100644 --- a/src/dvdnav/vmcmd.rs +++ b/src/dvdnav/vmcmd.rs @@ -297,16 +297,16 @@ mod tests { v.try_into().unwrap() } - // KATs taken from the real SOTL / Greenland discs (decoded in the PoC). + // KATs taken from real-world First-Play command streams. #[test] - fn greenland_first_play_is_jumptt_1() { + fn first_play_jumptt_1_decodes() { let c = decode(&h("3002000000010000")); assert_eq!(c.instr, Instr::JumpTt { ttn: 1 }); assert!(c.compare.is_none()); } #[test] - fn sotl_first_play_is_jumpss_vtsm_root() { + fn first_play_jumpss_vtsm_root_decodes() { // 30 06 ... byte5=0x83 -> sub 2 (VTSM), vts=byte4=1, menu=byte5&0xF=3 (root) let c = decode(&h("3006000101830000")); assert_eq!( @@ -320,8 +320,9 @@ mod tests { } #[test] - fn sotl_title_dispatch_is_conditional_linkpgn_2() { - // 20 a6 ... CmpLink: if GPRM0 == 2 -> LinkPGN 2 (cell 2 = the 5:02 start) + fn title_dispatch_is_conditional_linkpgn_2() { + // 20 a6 ... CmpLink: if GPRM0 == 2 -> LinkPGN 2 (the cell where the + // feature begins). let c = decode(&h("20a6000000020002")); assert_eq!(c.instr, Instr::LinkPgn { pgn: 2 }); let cmp = c.compare.expect("conditional"); @@ -332,7 +333,7 @@ mod tests { } #[test] - fn sotl_root_button_is_linkpgcn_37() { + fn root_button_is_linkpgcn_37() { assert_eq!( decode(&h("2004000000000025")).instr, Instr::LinkPgcn { pgcn: 37 } @@ -340,7 +341,7 @@ mod tests { } #[test] - fn greenland_scene_button_is_linkpgn() { + fn scene_button_is_linkpgn() { assert_eq!( decode(&h("2006000000001401")).instr, Instr::LinkPgn { pgn: 1 } @@ -356,7 +357,7 @@ mod tests { #[test] fn setgprm_immediate_mov() { - // SOTL First-Play pre[0]: 71 00 | reg=byte3=6 | imm(bytes4-5)=0x03e8 -> g6 = 1000 + // First-Play pre[0]: 71 00 | reg=byte3=6 | imm(bytes4-5)=0x03e8 -> g6 = 1000 match decode(&h("7100000603e80000")).instr { Instr::SetGprm { reg, diff --git a/src/ifo.rs b/src/ifo.rs index 0d249340..3a169b38 100644 --- a/src/ifo.rs +++ b/src/ifo.rs @@ -578,7 +578,7 @@ fn parse_vts( // values are in turn relative to `vtstt_vobs`. To turn them into the absolute // disc LBAs the reader needs, add the IFO file's own on-disc location (from // the UDF FS). Without this rebase every extent started `ifo_lba` sectors too - // early — for THESILENCEOFTHELAMBS the feature began at LBA 126 (the VMGI / + // early — on one real disc the feature began at LBA 126 (the VMGI / // VIDEO_TS.VOB main-menu region) instead of 132886 (VTS_03_1.VOB), so the // first ~4.5 min of muxed video was the disc's main menu before the stream // drifted into the movie. diff --git a/src/labels/deluxe.rs b/src/labels/deluxe.rs index 9ec70e9f..c1b3050f 100644 --- a/src/labels/deluxe.rs +++ b/src/labels/deluxe.rs @@ -68,7 +68,7 @@ //! per-stream arrays (filtered out by the array-parameter guard in the //! decoder). SDH/RNIB is encoded as a distinct Language VALUE //! ("English SDH", "English RNIB"), recovered into the qualifier from the -//! name. Grounded on the F&F cluster (fixtures from *Fast Five*). +//! name. Grounded on fixtures captured from a real Universal Blu-ray release. //! - **Disney/Warner**: 70-value Language enum, same binding shape; the //! original corpus this parser was written against. @@ -3211,9 +3211,10 @@ mod tests { assert_eq!(set.len(), 1); } - // ── Real-disc fixtures: Universal (studio="uni"), Fast Five ────────────── + // ── Real-disc fixtures: Universal (studio="uni") ───────────────────────── // - // Captured from `FastFive.iso` `/BDMV/JAR/00000.jar` (`com/bydeluxe/…`). + // Captured from a real Universal Blu-ray release's `/BDMV/JAR/00000.jar` + // (`com/bydeluxe/…`). // These are verbatim, unmodified `.class` files — the format we READ, never // execute — exercising the full Phase A→D pipeline against real obfuscated // Deluxe bytecode rather than synthetic fixtures. @@ -3335,7 +3336,8 @@ mod tests { #[test] fn universal_config_xml_studio_attribute_is_parsed() { - // The real config.xml from FastFive.iso /BDMV/JAR/99999/config.xml. + // A real config.xml from a Universal Blu-ray release's + // /BDMV/JAR/99999/config.xml. let xml = br#" -- "#; diff --git a/src/mpls.rs b/src/mpls.rs index fbb29481..b91649e6 100644 --- a/src/mpls.rs +++ b/src/mpls.rs @@ -4,7 +4,7 @@ //! Contains play items (clips) with in/out timestamps, //! stream info (video, audio, subtitle tracks). //! -//! Reference: https://github.com/lw/BluRay/wiki/MPLS +//! Format is documented in the BD-ROM PlayList (MPLS) specification. use crate::error::{Error, Result}; @@ -1028,8 +1028,8 @@ mod tests { } // ───────────────────────────────────────────────────────────────────── - // Added hardening tests below. Grounded in the BD-ROM MPLS spec - // (https://github.com/lw/BluRay/wiki/MPLS) byte layout. + // Added hardening tests below, grounded in the BD-ROM MPLS spec byte + // layout. // ───────────────────────────────────────────────────────────────────── /// Header guard: parse() requires `playlist_start + 10 <= data.len()` diff --git a/src/mux/codec/mpeg2.rs b/src/mux/codec/mpeg2.rs index 4727c96a..13fe684f 100644 --- a/src/mux/codec/mpeg2.rs +++ b/src/mux/codec/mpeg2.rs @@ -1183,7 +1183,7 @@ mod tests { #[test] fn opening_au_keeps_disc_pts_and_opening_seq_header_no_zero_floor() { - // SOTL SUB-TASK 2 regression (opening-GOP / still-frame open). A DVD + // Opening-GOP / still-frame open regression. A DVD // title opens on a VOBU that begins with a sequence header + I-frame; the // disc stamps that opening I-frame at its REAL (non-zero) timeline PTS, // not 0. The parser must (a) emit the opening I-frame with that real PTS diff --git a/src/mux/mkv.rs b/src/mux/mkv.rs index 6248a1f0..11ce5695 100644 --- a/src/mux/mkv.rs +++ b/src/mux/mkv.rs @@ -456,7 +456,7 @@ impl MkvTrack { // // rc.5.1 added it (= half the frame period, 20 ms for 576i25) to try // to fix the Windows-fps report, on the theory that Windows derives - // fps from it. The captured SOTL evidence proves the opposite: with + // fps from it. The captured real-media evidence proves the opposite: with // FlagInterlaced=1 + DefaultDuration=40 ms + DefaultDecodedFieldDuration=20 ms, // Windows Explorer reports 12.5 fps (half), and a media analyzer // flips the track to "Frame rate mode: Variable" with no clean rate. A @@ -1111,7 +1111,7 @@ impl MkvMuxer { // `field_duration_ns == 0` (see `MkvTrack::video`) so this element is // NOT written: emitting it (20 ms for 576i25) is exactly what made // Windows Explorer report 12.5 fps and a media analyzer flip to VFR on - // the captured SOTL rip, while a known-correct rip — which omits it — + // the captured rip, while a known-correct rip — which omits it — // shows the full 25 fps. The guard below is retained so a non-zero // value still emits // a well-formed element for any future caller / round-trip test, but @@ -4442,7 +4442,7 @@ mod tests { #[test] fn opening_keyframe_with_nonzero_disc_pts_anchors_base_not_corrupted() { - // SOTL SUB-TASK 2 regression (opening-GOP PTS handling). A DVD title + // Opening-GOP PTS handling regression. A DVD title // opens on an I-frame the disc stamps at its REAL timeline PTS (here // ~10 s, a large non-zero value — NOT 0). The muxer must anchor `base` on // that first kept keyframe so the first cluster's timestamp is 0 (the @@ -5556,11 +5556,11 @@ mod tests { #[test] fn interlaced_576i_omits_default_decoded_field_duration_keeps_full_frame_duration() { - // SOTL SUB-TASK 1 regression. The Windows-fps fix: a 576i25 track must + // Interlaced-fps signalling regression. The Windows-fps fix: a 576i25 track must // carry the FULL-FRAME DefaultDuration (40 ms → `1/DefaultDuration` = 25 // fps, the only rate every tool trusts) and must NOT emit // DefaultDecodedFieldDuration. rc.5.1 emitted the 20 ms field duration to - // try to fix Windows; the captured SOTL evidence proved it did the + // try to fix Windows; the captured real-media evidence proved it did the // opposite (Explorer 12.5 fps, analyzer VFR). A known-correct rip omits // it (Explorer 25 fps, analyzer CFR). So: frame duration present = 40 ms, // field duration ABSENT, interlace signalling (FlagInterlaced/FieldOrder) diff --git a/tests/crypto_tests.rs b/tests/crypto_tests.rs index ec0016f3..bfdd47ed 100644 --- a/tests/crypto_tests.rs +++ b/tests/crypto_tests.rs @@ -559,18 +559,18 @@ fn css_roundtrip_multiple_keys() { } } -// ── CSS Stevenson attack tests ───────────────────────────────────────────── +// ── CSS keyless recovery tests ───────────────────────────────────────────── -/// Attempt the Stevenson attack on synthetically scrambled sectors. +/// Attempt the keyless recovery on synthetically scrambled sectors. /// /// The CSS cipher on real DVDs stores ciphertext through a TAB1 output -/// layer that the Stevenson attack depends on. Synthetically scrambled +/// layer that the keyless recovery depends on. Synthetically scrambled /// sectors (produced by calling descramble_sector on plaintext) may not /// exhibit this relationship, so the attack is not guaranteed to converge /// on synthetic data. This test verifies that when the attack DOES return /// a key, that key correctly descrambles the sector. #[test] -fn css_stevenson_attack_validates_cracked_key() { +fn css_keyless_recovery_validates_cracked_key() { let candidates: &[([u8; 5], [u8; 5])] = &[ ( [0x42, 0x13, 0x37, 0xBE, 0xEF], @@ -617,7 +617,7 @@ fn css_stevenson_attack_validates_cracked_key() { css::lfsr::descramble_sector(key, &mut sector); sector[0x14] = 0x30; - let cracked = css::stevenson::crack_title_key(§or); + let cracked = css::keyless::crack_title_key(§or); if let Some(cracked_key) = cracked { let mut test = sector.clone(); @@ -635,7 +635,7 @@ fn css_stevenson_attack_validates_cracked_key() { any_cracked = true; eprintln!( - "Stevenson attack succeeded: key={:02X?} seed={:02X?} cracked={:02X?}", + "keyless recovery succeeded: key={:02X?} seed={:02X?} cracked={:02X?}", key, seed, cracked_key ); } @@ -643,7 +643,7 @@ fn css_stevenson_attack_validates_cracked_key() { if !any_cracked { eprintln!( - "Stevenson attack did not converge on any synthetic key/seed pair. \ + "keyless recovery did not converge on any synthetic key/seed pair. \ This is expected: synthetic sectors lack the TAB1 output encoding \ present in real CSS-encrypted DVD sectors." ); @@ -696,7 +696,7 @@ fn css_recover_title_key_with_exact_plaintext() { sector[0x14] = 0x30; // Recover with exact known plaintext - let recovered = css::stevenson::recover_title_key(§or, &pes_header); + let recovered = css::keyless::recover_title_key(§or, &pes_header); if let Some(rkey) = recovered { let mut test = sector.clone(); diff --git a/tests/pass_n_patch_fix.rs b/tests/pass_n_patch_fix.rs index f0ab5cf3..4867fa10 100644 --- a/tests/pass_n_patch_fix.rs +++ b/tests/pass_n_patch_fix.rs @@ -38,7 +38,7 @@ fn css_decrypt_of_an_uncrackable_sector_still_descrambles() { // and the cached key stays the best available evidence. // // This test previously asserted DecryptFailed, matching a round-9 change - // that made real DVDs unrippable (Greenland.iso). CSS is not AACS: an AACS + // that made real DVDs unrippable. CSS is not AACS: an AACS // unit key either opens a unit or does not, whereas a CSS title key is // recovered from data whose recoverability varies sector by sector. let mut sector = vec![0xFFu8; 2048];