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 48d70c092c
commit 926939f24b
3 changed files with 75 additions and 26 deletions
+23 -4
View File
@@ -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,
})
}
+47 -22
View File
@@ -16,9 +16,9 @@ use crate::platform::mt1959::Mt1959;
pub struct DriveSession {
scsi: Box<dyn ScsiTransport>,
driver: Box<dyn PlatformDriver>,
pub profile: DriveProfile,
pub platform: profile::Platform,
driver: Option<Box<dyn PlatformDriver>>,
pub profile: Option<DriveProfile>,
pub platform: Option<profile::Platform>,
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<usize> {
@@ -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));
}
}
+5
View File
@@ -44,6 +44,9 @@ pub struct DriveId {
/// Raw 96-byte INQUIRY response for additional parsing if needed.
pub raw_inquiry: Vec<u8>,
/// Raw GET CONFIGURATION Feature 010Ch response bytes.
pub raw_gc_010c: Vec<u8>,
}
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(),
}
}