Audit fixes + DVD support foundation (IFO, PS demux, MPEG-2, CSS crack)

Audit fixes (14 critical, 22 warnings):
- UDF: bounds checks on all ICB/FID parsing from disc data
- SCSI Linux: saturating_sub on residual, CDB length guard, buffer size guard
- SCSI macOS: SCSITaskStatus u32 (was u8 — stack corruption)
- AACS: EC mod_inv returns infinity instead of panic, key reduced mod n
- AACS: do_handshake tries all host certs (was returning on first failure)
- H.264: bounds check on SPS < 4 bytes
- ContentReader: error on missing unit key (was zero-fill)
- KEYDB: flat redirect loop (was recursive), 100MB response limit, Windows HOME fallback
- ISO writer: AVDP extent order, partition length, allocation cap
- Network: removed TCP_NODELAY on bulk stream
- MKV: guard on u64::MAX seek
- disc.rs: saturating_sub on extent offset, simplified dead region code
- cargo fmt (610 violations), cargo clippy --fix (55 auto-fixes)

DVD support (new files):
- src/ifo.rs — IFO parser (VIDEO_TS.IFO, VTS_XX_0.IFO, PGC chains, cells, streams) — 13 tests
- src/mux/ps.rs — MPEG-2 Program Stream demuxer (pack headers, PES, private stream 1) — 12 tests
- src/mux/codec/mpeg2.rs — MPEG-2 video parser (sequence headers, I-frame detection) — 15 tests
- src/css/crack.rs — split-attack algorithm (LFSR cipher needs verification — test ignored)

226 tests total (was 186), 1 ignored (CSS crack needs cipher verification).
This commit is contained in:
MattJackson
2026-04-11 16:52:22 +00:00
parent 6de4ee4b5c
commit 01235ff347
57 changed files with 6189 additions and 1519 deletions
+70 -26
View File
@@ -5,14 +5,16 @@
//!
//! Token format: `{lang}_{codec?}_{purpose?}_{region?}_`
use super::{vocab, LabelPurpose, LabelQualifier, StreamLabel, StreamLabelType};
use crate::sector::SectorReader;
use crate::udf::UdfFs;
use super::{StreamLabel, StreamLabelType, LabelPurpose, LabelQualifier, vocab};
/// Known audio codec tokens
const AUDIO_CODECS: &[&str] = &["MLP", "AC3", "DTS", "DDL", "WAV", "AC"];
/// Known region tokens
const REGIONS: &[&str] = &["US", "UK", "CF", "PF", "CS", "LS", "BP", "PP", "SM", "TM", "CAN", "DUM", "FLE"];
const REGIONS: &[&str] = &[
"US", "UK", "CF", "PF", "CS", "LS", "BP", "PP", "SM", "TM", "CAN", "DUM", "FLE",
];
pub fn detect(udf: &UdfFs) -> bool {
super::jar_file_exists(udf, "bluray_project.bin")
@@ -30,7 +32,9 @@ pub fn parse(reader: &mut dyn SectorReader, udf: &UdfFs) -> Option<Vec<StreamLab
for s in &strings {
// Detect feature section start
if s.starts_with("FPL_") || s.starts_with("SEG_MainFeature") {
if in_feature { break; }
if in_feature {
break;
}
in_feature = true;
audio_num = 0;
sub_num = 0;
@@ -42,30 +46,42 @@ pub fn parse(reader: &mut dyn SectorReader, udf: &UdfFs) -> Option<Vec<StreamLab
break;
}
if !in_feature { continue; }
if !in_feature {
continue;
}
if let Some(label) = parse_token(s) {
match label.stream_type {
StreamLabelType::Audio => {
audio_num += 1;
labels.push(StreamLabel { stream_number: audio_num, ..label });
labels.push(StreamLabel {
stream_number: audio_num,
..label
});
}
StreamLabelType::Subtitle => {
sub_num += 1;
labels.push(StreamLabel { stream_number: sub_num, ..label });
labels.push(StreamLabel {
stream_number: sub_num,
..label
});
}
}
}
}
if labels.is_empty() { return None; }
if labels.is_empty() {
return None;
}
Some(labels)
}
fn parse_token(s: &str) -> Option<StreamLabel> {
let clean = s.trim().trim_start_matches('\t').trim_end_matches('_');
let parts: Vec<&str> = clean.split('_').collect();
if parts.len() < 2 { return None; }
if parts.len() < 2 {
return None;
}
let lang = parts[0];
if lang.len() != 3 || !lang.chars().all(|c| c.is_ascii_lowercase()) {
@@ -80,26 +96,54 @@ fn parse_token(s: &str) -> Option<StreamLabel> {
let mut is_audio = false;
for &part in &parts[1..] {
if part.is_empty() { continue; }
if AUDIO_CODECS.contains(&part) { codec = vocab::codec(part).to_string(); is_audio = true; }
else if part == "ADES" { purpose = LabelPurpose::Descriptive; is_audio = true; }
else if part == "ACOM" { purpose = LabelPurpose::Commentary; is_audio = true; }
else if part == "ADLG" { is_audio = true; }
else if part == "ATRI" { is_audio = true; }
else if part == "SDH" { qualifier = LabelQualifier::Sdh; is_subtitle = true; }
else if part == "SDLG" { is_subtitle = true; }
else if part == "SCOM" { purpose = LabelPurpose::Commentary; is_subtitle = true; }
else if part == "STRI" { is_subtitle = true; }
else if part == "TXT" { is_subtitle = true; }
else if part == "FOR" { qualifier = LabelQualifier::Forced; }
else if REGIONS.contains(&part) { variant = part.to_string(); }
else if part.starts_with("PGStream") { is_subtitle = true; }
else { return None; }
if part.is_empty() {
continue;
}
if AUDIO_CODECS.contains(&part) {
codec = vocab::codec(part).to_string();
is_audio = true;
} else if part == "ADES" {
purpose = LabelPurpose::Descriptive;
is_audio = true;
} else if part == "ACOM" {
purpose = LabelPurpose::Commentary;
is_audio = true;
} else if part == "ADLG" {
is_audio = true;
} else if part == "ATRI" {
is_audio = true;
} else if part == "SDH" {
qualifier = LabelQualifier::Sdh;
is_subtitle = true;
} else if part == "SDLG" {
is_subtitle = true;
} else if part == "SCOM" {
purpose = LabelPurpose::Commentary;
is_subtitle = true;
} else if part == "STRI" {
is_subtitle = true;
} else if part == "TXT" {
is_subtitle = true;
} else if part == "FOR" {
qualifier = LabelQualifier::Forced;
} else if REGIONS.contains(&part) {
variant = part.to_string();
} else if part.starts_with("PGStream") {
is_subtitle = true;
} else {
return None;
}
}
if !is_audio && !is_subtitle { return None; }
if !is_audio && !is_subtitle {
return None;
}
let stream_type = if is_subtitle { StreamLabelType::Subtitle } else { StreamLabelType::Audio };
let stream_type = if is_subtitle {
StreamLabelType::Subtitle
} else {
StreamLabelType::Audio
};
Some(StreamLabel {
stream_number: 0,
@@ -118,7 +162,7 @@ fn extract_strings(data: &[u8]) -> Vec<String> {
let mut current = String::new();
for &b in data {
if b >= 0x20 && b < 0x7f {
if (0x20..0x7f).contains(&b) {
current.push(b as char);
} else {
if current.len() > 3 {