open() works on all drives, AACS 2.0 handshake wired, raw_gc_010c on DriveId

- 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
This commit is contained in:
MattJackson
2026-04-10 08:48:41 -07:00
parent 5990ec9566
commit c3e50f3a2e
3 changed files with 75 additions and 26 deletions
+23 -4
View File
@@ -564,12 +564,31 @@ impl Disc {
.ok(); .ok();
let mkb_ver = mkb_data.as_deref().and_then(aacs::mkb_version); 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 // Resolve: disc hash → KEYDB lookup → VUK → unit keys
let vid_zero = [0u8; 16];
let resolved = aacs::resolve_keys( let resolved = aacs::resolve_keys(
&uk_ro_data, &uk_ro_data,
cc_data.as_deref(), cc_data.as_deref(),
&vid_zero, &volume_id,
&keydb, &keydb,
mkb_data.as_deref(), mkb_data.as_deref(),
).ok_or_else(|| Error::AacsNoKeys)?; ).ok_or_else(|| Error::AacsNoKeys)?;
@@ -588,8 +607,8 @@ impl Disc {
}, },
vuk: resolved.vuk, vuk: resolved.vuk,
unit_keys: resolved.unit_keys, unit_keys: resolved.unit_keys,
read_data_key: None, read_data_key,
volume_id: [0u8; 16], volume_id,
}) })
} }
+47 -22
View File
@@ -16,9 +16,9 @@ use crate::platform::mt1959::Mt1959;
pub struct DriveSession { pub struct DriveSession {
scsi: Box<dyn ScsiTransport>, scsi: Box<dyn ScsiTransport>,
driver: Box<dyn PlatformDriver>, driver: Option<Box<dyn PlatformDriver>>,
pub profile: DriveProfile, pub profile: Option<DriveProfile>,
pub platform: profile::Platform, pub platform: Option<profile::Platform>,
pub drive_id: DriveId, pub drive_id: DriveId,
device_path: String, device_path: String,
} }
@@ -29,25 +29,31 @@ impl DriveSession {
let profiles = profile::load_bundled()?; let profiles = profile::load_bundled()?;
let drive_id = DriveId::from_drive(transport.as_mut())?; let drive_id = DriveId::from_drive(transport.as_mut())?;
let m = profile::find_by_drive_id(&profiles, &drive_id) let m = profile::find_by_drive_id(&profiles, &drive_id);
.ok_or_else(|| Error::UnsupportedDrive { let (driver, platform, profile) = match m {
vendor_id: drive_id.vendor_id.trim().to_string(), Some(m) => (
product_id: drive_id.product_id.trim().to_string(), create_driver(m.platform, &m.profile).ok(),
product_revision: drive_id.product_revision.trim().to_string(), Some(m.platform),
})?; Some(m.profile),
),
let driver = create_driver(m.platform, &m.profile)?; None => (None, None, None),
};
Ok(DriveSession { Ok(DriveSession {
scsi: transport, scsi: transport,
driver, driver,
platform: m.platform, platform,
profile: m.profile, profile,
drive_id, drive_id,
device_path: device.to_string_lossy().to_string(), 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<()> { pub fn wait_ready(&mut self) -> Result<()> {
let tur = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; let tur = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
for _ in 0..60 { for _ in 0..60 {
@@ -65,27 +71,48 @@ impl DriveSession {
} }
pub fn platform_name(&self) -> &str { 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 { pub fn device_path(&self) -> &str {
&self.device_path &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<()> { 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 /// Probe disc surface so the drive firmware learns optimal read speeds
/// per region. After this the host reads at max speed and the drive /// per region. After this the host reads at max speed and the drive
/// manages zones internally. /// manages zones internally.
pub fn probe_disc(&mut self) -> Result<()> { 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 { 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<usize> { pub fn read_disc(&mut self, lba: u32, count: u16, buf: &mut [u8]) -> Result<usize> {
@@ -140,10 +167,8 @@ pub fn find_drives() -> Vec<(String, DriveId)> {
if !std::path::Path::new(&path).exists() { continue; } if !std::path::Path::new(&path).exists() { continue; }
if let Ok(mut transport) = crate::scsi::open(std::path::Path::new(&path)) { if let Ok(mut transport) = crate::scsi::open(std::path::Path::new(&path)) {
if let Ok(id) = DriveId::from_drive(transport.as_mut()) { if let Ok(id) = DriveId::from_drive(transport.as_mut()) {
let profiles = match profile::load_bundled() { // Include all optical drives (peripheral device type 0x05)
Ok(p) => p, Err(_) => continue, if id.raw_inquiry.len() > 0 && (id.raw_inquiry[0] & 0x1F) == 0x05 {
};
if profile::find_by_drive_id(&profiles, &id).is_some() {
drives.push((path, id)); drives.push((path, id));
} }
} }
+5
View File
@@ -44,6 +44,9 @@ pub struct DriveId {
/// Raw 96-byte INQUIRY response for additional parsing if needed. /// Raw 96-byte INQUIRY response for additional parsing if needed.
pub raw_inquiry: Vec<u8>, pub raw_inquiry: Vec<u8>,
/// Raw GET CONFIGURATION Feature 010Ch response bytes.
pub raw_gc_010c: Vec<u8>,
} }
impl DriveId { impl DriveId {
@@ -84,6 +87,7 @@ impl DriveId {
firmware_date, firmware_date,
serial_number, serial_number,
raw_inquiry: inquiry.to_vec(), 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(), firmware_date: firmware_date.to_string(),
serial_number: String::new(), serial_number: String::new(),
raw_inquiry: inquiry.to_vec(), raw_inquiry: inquiry.to_vec(),
raw_gc_010c: Vec::new(),
} }
} }