From 8bc1de6c9b1383799f15b1e34aa414a7bc8fe06a Mon Sep 17 00:00:00 2001 From: MattJackson <1085847+MattJackson@users.noreply.github.com> Date: Thu, 4 Jun 2026 13:20:27 -0700 Subject: [PATCH] 0.27.5 (step 2, Phase 1): expose AACS inputs (uk_ro, mkb) on AacsState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit scan now stashes the raw Unit_Key_RO.inf + MKB bytes on AacsState (via resolve_vid_only, the disable_keydb path), so an external key-resolver can derive unit keys from a resolved VUK without re-reading the disc — the foundation for moving lookup/derivation out of libfreemkv. Additive: the keydb path is untouched, all existing constructors default the new fields empty. 584 lib tests green. Builds on the KeyOrigin rename + the Key/decrypt_with API. --- Cargo.toml | 2 +- src/disc/encrypt.rs | 12 +++++++++--- src/disc/mod.rs | 13 ++++++++++++- 3 files changed, 22 insertions(+), 5 deletions(-) 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(), } }