diff --git a/Cargo.toml b/Cargo.toml index 3631dfb..15bfeb1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libfreemkv" -version = "0.27.4" +version = "0.27.5" edition = "2024" rust-version = "1.86" license = "AGPL-3.0-only" diff --git a/src/disc/encrypt.rs b/src/disc/encrypt.rs index edf526e..168bba8 100644 --- a/src/disc/encrypt.rs +++ b/src/disc/encrypt.rs @@ -465,6 +465,8 @@ impl Disc { unit_keys: resolved.unit_keys, read_data_key, volume_id, + uk_ro: Vec::new(), + mkb: Vec::new(), }) } @@ -527,6 +529,8 @@ impl Disc { unit_keys: vec![(1, unit_key)], read_data_key: handshake.and_then(|h| h.read_data_key), volume_id: handshake.map(|h| h.volume_id).unwrap_or([0u8; 16]), + uk_ro: Vec::new(), + mkb: Vec::new(), }) } @@ -563,12 +567,12 @@ impl Disc { None => 1, }; // MKB_RO is the correctly-sized copy; avoid reading the padded RW region. - let mkb_ver = udf_fs + let mkb_bytes = udf_fs .read_file(reader, "/AACS/MKB_RO.inf") .or_else(|_| udf_fs.read_file(reader, "/AACS/MKB_RW.inf")) .ok() - .as_deref() - .and_then(aacs::mkb_version); + .unwrap_or_default(); + let mkb_ver = aacs::mkb_version(&mkb_bytes); tracing::warn!( target: "freemkv::disc", @@ -590,6 +594,8 @@ impl Disc { unit_keys: vec![], read_data_key: handshake.and_then(|h| h.read_data_key), volume_id: handshake.map(|h| h.volume_id).unwrap_or([0u8; 16]), + uk_ro: uk_ro_data, + mkb: mkb_bytes, }) } } diff --git a/src/disc/mod.rs b/src/disc/mod.rs index 3e96920..e46d771 100644 --- a/src/disc/mod.rs +++ b/src/disc/mod.rs @@ -893,6 +893,13 @@ pub struct AacsState { pub read_data_key: Option<[u8; 16]>, /// Volume ID (16 bytes) -- from SCSI handshake pub volume_id: [u8; 16], + /// Raw `Unit_Key_RO.inf` bytes (encrypted unit keys + CPS map). Stashed at + /// scan so an external resolver (key-resolver) can derive the unit keys + /// from a VUK without re-reading the disc. Empty when not captured. + pub uk_ro: Vec, + /// Raw MKB bytes (`MKB_RO.inf`). Stashed at scan so an external resolver can + /// walk it (device/processing key → media key). Empty when not captured. + pub mkb: Vec, } /// How AACS keys were resolved. Variants are ordered root-of-trust → @@ -1555,7 +1562,9 @@ impl Disc { unit_keys: keys, read_data_key: None, volume_id: [0u8; 16], - }); + uk_ro: Vec::new(), + mkb: Vec::new(), + }); // The prior resolution error (e.g. KeydbLoad) is now moot — we have // the decryption key. Clear it so callers don't treat the disc as // keyless on the stale error. @@ -2833,6 +2842,8 @@ mod tests { unit_keys, read_data_key: None, volume_id: [0u8; 16], + uk_ro: Vec::new(), + mkb: Vec::new(), } }