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