aacs: discover HD DVD AACS dir + title-key files instead of hardcoding /ANY!/VTKF000
The HD DVD AACS directory name and title-key filename are chosen by the authoring house, but the resolver hardcoded a single spelling (/ANY!/VTKF000.AACS, /ANY!/MKBROM.AACS, /ANY!/CONTENT_CERT.AACS). Real discs diverge: Freedom (Memory-Tech) names its AACS dir AAC! and ships VTKF090.AACS + VTKF100.AACS; Harry Potter carries VTKF000/001/002/099. On such a disc the hardcoded path finds nothing, so no MKB/title-key/cert is read and decryption silently can't engage. Replace the fixed HD DVD path constants with structural discovery: - find_hddvd_aacs_dir() locates the AACS dir as the root child dir ending in '!' that contains MKBROM.AACS (so the ..._BAK mirror is skipped; the dozens of decoy advanced-content '!' dirs are excluded by the MKBROM.AACS guard). - role_paths(udf, role) builds the ordered candidate list per role: the static BD/UHD /AACS/ paths first, then the discovered HD DVD files — MKBROM.AACS, CONTENT_CERT.AACS, and every VTKF*.AACS (sorted), not just VTKF000. - read_first() is now generic over &str / String so it takes the Vec<String>. BD/UHD unaffected (no '!' dir → discovery returns None, list is the /AACS/ constants exactly as before). Verified on real Freedom (AAC!/VTKF090+100) and Dukes (ANY!/VTKF000) ISOs; unit tests cover both shapes. Open item (TODO(hddvd-encrypted)): when a disc has multiple VTKF variants the correct one must be chosen by validating its VUK-derived key against a real encrypted unit rather than first-that-reads. Blocked on obtaining a genuinely encrypted HD DVD image — all HD DVD ISOs on hand are already-decrypted rips.
This commit is contained in:
+10
-5
@@ -324,13 +324,18 @@ impl Disc {
|
||||
use crate::aacs;
|
||||
|
||||
let uk_ro_data =
|
||||
aacs::read_first(aacs::UNIT_KEY_RO_PATHS, |p| udf_fs.read_file(reader, p))?;
|
||||
aacs::read_first(&aacs::role_paths(udf_fs, aacs::AacsRole::UnitKey), |p| {
|
||||
udf_fs.read_file(reader, p)
|
||||
})?;
|
||||
let dh = aacs::inf::disc_hash(&uk_ro_data);
|
||||
|
||||
let cc = aacs::read_first(aacs::CONTENT_CERT_PATHS, |p| udf_fs.read_file(reader, p))
|
||||
.ok()
|
||||
.as_deref()
|
||||
.and_then(aacs::inf::parse_content_cert);
|
||||
let cc = aacs::read_first(
|
||||
&aacs::role_paths(udf_fs, aacs::AacsRole::ContentCert),
|
||||
|p| udf_fs.read_file(reader, p),
|
||||
)
|
||||
.ok()
|
||||
.as_deref()
|
||||
.and_then(aacs::inf::parse_content_cert);
|
||||
let bus_encryption = cc.as_ref().map(|c| c.bus_encryption).unwrap_or(false);
|
||||
// No-cert default = UHD (V20 stride), matching `read_aacs_version` so the
|
||||
// scanned `AacsState.version` and the out-of-band fetch agree. A wrong
|
||||
|
||||
+12
-9
@@ -1824,9 +1824,10 @@ impl Disc {
|
||||
reader: &mut dyn SectorSource,
|
||||
udf_fs: &udf::UdfFs,
|
||||
) -> Result<(Vec<u8>, Vec<u8>, u8)> {
|
||||
let inf = crate::aacs::read_first(crate::aacs::UNIT_KEY_RO_PATHS, |p| {
|
||||
udf_fs.read_file(reader, p)
|
||||
})?;
|
||||
let inf = crate::aacs::read_first(
|
||||
&crate::aacs::role_paths(udf_fs, crate::aacs::AacsRole::UnitKey),
|
||||
|p| udf_fs.read_file(reader, p),
|
||||
)?;
|
||||
let mkb = Self::read_mkb_content(reader, udf_fs)?;
|
||||
let version = Self::read_aacs_version(reader, udf_fs);
|
||||
Ok((inf, mkb, version))
|
||||
@@ -1844,9 +1845,10 @@ impl Disc {
|
||||
/// mis-strided title keys (silent wrong unit keys), so a missing cert must
|
||||
/// not quietly pick the V10 stride for a UHD disc.
|
||||
fn read_aacs_version(reader: &mut dyn SectorSource, udf_fs: &udf::UdfFs) -> u8 {
|
||||
match crate::aacs::read_first(crate::aacs::CONTENT_CERT_PATHS, |p| {
|
||||
udf_fs.read_file(reader, p)
|
||||
})
|
||||
match crate::aacs::read_first(
|
||||
&crate::aacs::role_paths(udf_fs, crate::aacs::AacsRole::ContentCert),
|
||||
|p| udf_fs.read_file(reader, p),
|
||||
)
|
||||
.ok()
|
||||
.as_deref()
|
||||
.and_then(crate::aacs::inf::parse_content_cert)
|
||||
@@ -1880,9 +1882,10 @@ impl Disc {
|
||||
const MAX_BYTES: usize = 64 * 1024 * 1024;
|
||||
let mut want = START_BYTES;
|
||||
loop {
|
||||
let buf = crate::aacs::read_first(crate::aacs::MKB_PATHS, |p| {
|
||||
udf_fs.read_file_prefix(reader, p, want)
|
||||
})?;
|
||||
let buf = crate::aacs::read_first(
|
||||
&crate::aacs::role_paths(udf_fs, crate::aacs::AacsRole::Mkb),
|
||||
|p| udf_fs.read_file_prefix(reader, p, want),
|
||||
)?;
|
||||
let n = crate::aacs::mkb::mkb_content_len(&buf);
|
||||
// `n` strictly inside `buf` => the record walk reached the padding
|
||||
// boundary (full content captured). `buf` shorter than `want` =>
|
||||
|
||||
Reference in New Issue
Block a user