From c3e50f3a2e5a14a4cbe144ae2832cd4a47d84fb0 Mon Sep 17 00:00:00 2001 From: MattJackson <1085847+MattJackson@users.noreply.github.com> Date: Fri, 10 Apr 2026 08:48:41 -0700 Subject: [PATCH] open() works on all drives, AACS 2.0 handshake wired, raw_gc_010c on DriveId MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DriveSession::open() no longer requires profile match — works on any optical drive - init()/probe_disc() return error gracefully for unknown drives - find_drives() returns all optical drives (PDT 0x05), not just profile-matched - has_profile() check for callers - AACS 2.0: handshake wired into resolve_aacs() — real VID + read_data_key - DriveId: added raw_gc_010c field for GET_CONFIG 010C response bytes --- src/disc.rs | 27 ++++++++++++++++--- src/drive.rs | 69 +++++++++++++++++++++++++++++++++---------------- src/identity.rs | 5 ++++ 3 files changed, 75 insertions(+), 26 deletions(-) diff --git a/src/disc.rs b/src/disc.rs index 65d5764..a24d44e 100644 --- a/src/disc.rs +++ b/src/disc.rs @@ -564,12 +564,31 @@ impl Disc { .ok(); let mkb_ver = mkb_data.as_deref().and_then(aacs::mkb_version); + // AACS SCSI handshake — get Volume ID (and read data key for AACS 2.0) + let mut volume_id = [0u8; 16]; + let mut read_data_key = None; + + if let Some(ref hc) = keydb.host_cert { + if let Ok(mut auth) = aacs::handshake::aacs_authenticate( + session, &hc.private_key, &hc.certificate, + ) { + // Read Volume ID (needed for MK → VUK derivation) + if let Ok(vid) = aacs::handshake::read_volume_id(session, &mut auth) { + volume_id = vid; + } + + // Read data keys for bus decryption (AACS 2.0 / UHD) + if let Ok((rdk, _wdk)) = aacs::handshake::read_data_keys(session, &mut auth) { + read_data_key = Some(rdk); + } + } + } + // Resolve: disc hash → KEYDB lookup → VUK → unit keys - let vid_zero = [0u8; 16]; let resolved = aacs::resolve_keys( &uk_ro_data, cc_data.as_deref(), - &vid_zero, + &volume_id, &keydb, mkb_data.as_deref(), ).ok_or_else(|| Error::AacsNoKeys)?; @@ -588,8 +607,8 @@ impl Disc { }, vuk: resolved.vuk, unit_keys: resolved.unit_keys, - read_data_key: None, - volume_id: [0u8; 16], + read_data_key, + volume_id, }) } diff --git a/src/drive.rs b/src/drive.rs index b8a55fd..c4a8d56 100644 --- a/src/drive.rs +++ b/src/drive.rs @@ -16,9 +16,9 @@ use crate::platform::mt1959::Mt1959; pub struct DriveSession { scsi: Box, - driver: Box, - pub profile: DriveProfile, - pub platform: profile::Platform, + driver: Option>, + pub profile: Option, + pub platform: Option, pub drive_id: DriveId, device_path: String, } @@ -29,25 +29,31 @@ impl DriveSession { let profiles = profile::load_bundled()?; let drive_id = DriveId::from_drive(transport.as_mut())?; - let m = profile::find_by_drive_id(&profiles, &drive_id) - .ok_or_else(|| Error::UnsupportedDrive { - vendor_id: drive_id.vendor_id.trim().to_string(), - product_id: drive_id.product_id.trim().to_string(), - product_revision: drive_id.product_revision.trim().to_string(), - })?; - - let driver = create_driver(m.platform, &m.profile)?; + let m = profile::find_by_drive_id(&profiles, &drive_id); + let (driver, platform, profile) = match m { + Some(m) => ( + create_driver(m.platform, &m.profile).ok(), + Some(m.platform), + Some(m.profile), + ), + None => (None, None, None), + }; Ok(DriveSession { scsi: transport, driver, - platform: m.platform, - profile: m.profile, + platform, + profile, drive_id, device_path: device.to_string_lossy().to_string(), }) } + /// Whether this drive has a known profile (unlock parameters available). + pub fn has_profile(&self) -> bool { + self.profile.is_some() + } + pub fn wait_ready(&mut self) -> Result<()> { let tur = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; for _ in 0..60 { @@ -65,27 +71,48 @@ impl DriveSession { } pub fn platform_name(&self) -> &str { - self.platform.name() + match self.platform { + Some(ref p) => p.name(), + None => "Unknown", + } } pub fn device_path(&self) -> &str { &self.device_path } - /// Initialize drive — unlock + firmware upload. Removes riplock. + /// Initialize drive — unlock + firmware upload. + /// Optional. Adds features: removes riplock, enables UHD reads, speed control. pub fn init(&mut self) -> Result<()> { - self.driver.init(self.scsi.as_mut()) + match self.driver { + Some(ref mut d) => d.init(self.scsi.as_mut()), + None => Err(Error::UnsupportedDrive { + vendor_id: self.drive_id.vendor_id.trim().to_string(), + product_id: self.drive_id.product_id.trim().to_string(), + product_revision: self.drive_id.product_revision.trim().to_string(), + }), + } } /// Probe disc surface so the drive firmware learns optimal read speeds /// per region. After this the host reads at max speed and the drive /// manages zones internally. pub fn probe_disc(&mut self) -> Result<()> { - self.driver.probe_disc(self.scsi.as_mut()) + match self.driver { + Some(ref mut d) => d.probe_disc(self.scsi.as_mut()), + None => Err(Error::UnsupportedDrive { + vendor_id: self.drive_id.vendor_id.trim().to_string(), + product_id: self.drive_id.product_id.trim().to_string(), + product_revision: self.drive_id.product_revision.trim().to_string(), + }), + } } pub fn is_ready(&self) -> bool { - self.driver.is_ready() + match self.driver { + Some(ref d) => d.is_ready(), + None => false, + } } pub fn read_disc(&mut self, lba: u32, count: u16, buf: &mut [u8]) -> Result { @@ -140,10 +167,8 @@ pub fn find_drives() -> Vec<(String, DriveId)> { if !std::path::Path::new(&path).exists() { continue; } if let Ok(mut transport) = crate::scsi::open(std::path::Path::new(&path)) { if let Ok(id) = DriveId::from_drive(transport.as_mut()) { - let profiles = match profile::load_bundled() { - Ok(p) => p, Err(_) => continue, - }; - if profile::find_by_drive_id(&profiles, &id).is_some() { + // Include all optical drives (peripheral device type 0x05) + if id.raw_inquiry.len() > 0 && (id.raw_inquiry[0] & 0x1F) == 0x05 { drives.push((path, id)); } } diff --git a/src/identity.rs b/src/identity.rs index b8dfa3d..e3913f4 100644 --- a/src/identity.rs +++ b/src/identity.rs @@ -44,6 +44,9 @@ pub struct DriveId { /// Raw 96-byte INQUIRY response for additional parsing if needed. pub raw_inquiry: Vec, + + /// Raw GET CONFIGURATION Feature 010Ch response bytes. + pub raw_gc_010c: Vec, } impl DriveId { @@ -84,6 +87,7 @@ impl DriveId { firmware_date, serial_number, raw_inquiry: inquiry.to_vec(), + raw_gc_010c: gc[..result.bytes_transferred].to_vec(), }) } @@ -98,6 +102,7 @@ impl DriveId { firmware_date: firmware_date.to_string(), serial_number: String::new(), raw_inquiry: inquiry.to_vec(), + raw_gc_010c: Vec::new(), } }