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:
+23
-4
@@ -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
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user