aacs: handshake primitives operate on &mut dyn ScsiTransport

The AACS cert-auth primitives (aacs_authenticate, the AACS 2.0 P-256
variants, read_volume_id, read_data_keys) and their scsi_read/scsi_write
helpers touched the drive ONLY through Drive::scsi_execute — a pure
pass-through to the transport. Thread &mut dyn ScsiTransport instead of
&mut Drive so these primitives are transport-level, matching the firmware
Unlocker seam (which hands out &mut dyn ScsiTransport for testability).
Pure mechanical signature change, no logic change; the cert orchestrator
(do_handshake_cert) keeps &mut Drive for the OEM-VID shortcut and passes
session.scsi_mut() into the primitives. Step toward making the cert
handshake a uniform registry unlocker.
This commit is contained in:
Matthew Jackson
2026-06-29 15:47:30 -07:00
parent 93fbfac6f0
commit f682405973
2 changed files with 34 additions and 26 deletions
+13 -11
View File
@@ -18,9 +18,8 @@
//! - AACS 2.0: drives accept AACS 1.0 host certs for backward compatibility //! - AACS 2.0: drives accept AACS 1.0 host certs for backward compatibility
//! (full P-256/SHA-256 AACS 2.0 handshake prepared but rarely needed) //! (full P-256/SHA-256 AACS 2.0 handshake prepared but rarely needed)
use crate::drive::Drive;
use crate::error::{Error, Result}; use crate::error::{Error, Result};
use crate::scsi::DataDirection; use crate::scsi::{DataDirection, ScsiTransport};
use num_bigint::BigUint; use num_bigint::BigUint;
use num_traits::{One, Zero}; use num_traits::{One, Zero};
use sha1::{Digest, Sha1}; use sha1::{Digest, Sha1};
@@ -45,16 +44,16 @@ fn handshake_err(err: Error, fallback: Error) -> Error {
} }
/// Execute a SCSI command that reads data from the device. /// Execute a SCSI command that reads data from the device.
fn scsi_read(session: &mut Drive, cdb: &[u8], len: usize) -> Result<Vec<u8>> { fn scsi_read(session: &mut dyn ScsiTransport, cdb: &[u8], len: usize) -> Result<Vec<u8>> {
let mut buf = vec![0u8; len]; let mut buf = vec![0u8; len];
session.scsi_execute(cdb, DataDirection::FromDevice, &mut buf, 5_000)?; session.execute(cdb, DataDirection::FromDevice, &mut buf, 5_000)?;
Ok(buf) Ok(buf)
} }
/// Execute a SCSI command that writes data to the device. /// Execute a SCSI command that writes data to the device.
fn scsi_write(session: &mut Drive, cdb: &[u8], data: &[u8]) -> Result<()> { fn scsi_write(session: &mut dyn ScsiTransport, cdb: &[u8], data: &[u8]) -> Result<()> {
let mut buf = data.to_vec(); let mut buf = data.to_vec();
session.scsi_execute(cdb, DataDirection::ToDevice, &mut buf, 5_000)?; session.execute(cdb, DataDirection::ToDevice, &mut buf, 5_000)?;
Ok(()) Ok(())
} }
@@ -881,7 +880,7 @@ impl std::fmt::Debug for AacsAuth {
/// Requires a host private key (20 bytes) and host certificate (92 bytes) /// Requires a host private key (20 bytes) and host certificate (92 bytes)
/// from the KEYDB.cfg HC entry. /// from the KEYDB.cfg HC entry.
pub fn aacs_authenticate( pub fn aacs_authenticate(
session: &mut Drive, session: &mut dyn ScsiTransport,
host_priv_key: &[u8; 20], host_priv_key: &[u8; 20],
host_cert: &[u8], host_cert: &[u8],
) -> Result<AacsAuth> { ) -> Result<AacsAuth> {
@@ -1018,7 +1017,7 @@ pub fn aacs_authenticate(
/// Falls back to aacs_authenticate (AACS 1.0) if AACS 2.0 host credentials /// Falls back to aacs_authenticate (AACS 1.0) if AACS 2.0 host credentials
/// are not available. /// are not available.
pub fn aacs2_authenticate( pub fn aacs2_authenticate(
session: &mut Drive, session: &mut dyn ScsiTransport,
host_priv_key_v1: &[u8; 20], host_priv_key_v1: &[u8; 20],
host_cert_v1: &[u8], host_cert_v1: &[u8],
host_priv_key_v2: Option<&[u8; 32]>, host_priv_key_v2: Option<&[u8; 32]>,
@@ -1044,7 +1043,7 @@ pub fn aacs2_authenticate(
/// Native AACS 2.0 handshake using P-256/SHA-256. /// Native AACS 2.0 handshake using P-256/SHA-256.
/// Same SCSI protocol, larger payloads (32-byte keys, 132-byte certs). /// Same SCSI protocol, larger payloads (32-byte keys, 132-byte certs).
fn aacs2_authenticate_p256( fn aacs2_authenticate_p256(
session: &mut Drive, session: &mut dyn ScsiTransport,
host_priv_key: &[u8; 32], host_priv_key: &[u8; 32],
host_cert: &[u8], host_cert: &[u8],
) -> Result<AacsAuth> { ) -> Result<AacsAuth> {
@@ -1169,7 +1168,7 @@ fn aacs2_authenticate_p256(
} }
/// Read Volume ID after successful authentication. /// Read Volume ID after successful authentication.
pub fn read_volume_id(session: &mut Drive, auth: &mut AacsAuth) -> Result<[u8; 16]> { pub fn read_volume_id(session: &mut dyn ScsiTransport, auth: &mut AacsAuth) -> Result<[u8; 16]> {
// REPORT DISC STRUCTURE format 0x80 // REPORT DISC STRUCTURE format 0x80
let cdb = cdb_report_disc_structure(auth.agid, 0x80, 36); let cdb = cdb_report_disc_structure(auth.agid, 0x80, 36);
let response = let response =
@@ -1191,7 +1190,10 @@ pub fn read_volume_id(session: &mut Drive, auth: &mut AacsAuth) -> Result<[u8; 1
} }
/// Read data keys after successful authentication (for AACS 2.0 bus encryption). /// Read data keys after successful authentication (for AACS 2.0 bus encryption).
pub fn read_data_keys(session: &mut Drive, auth: &mut AacsAuth) -> Result<([u8; 16], [u8; 16])> { pub fn read_data_keys(
session: &mut dyn ScsiTransport,
auth: &mut AacsAuth,
) -> Result<([u8; 16], [u8; 16])> {
// REPORT DISC STRUCTURE format 0x84 // REPORT DISC STRUCTURE format 0x84
let cdb = cdb_report_disc_structure(auth.agid, 0x84, 36); let cdb = cdb_report_disc_structure(auth.agid, 0x84, 36);
let response = let response =
+9 -3
View File
@@ -118,9 +118,14 @@ impl AacsCertUnlocker<'_> {
if idx > 0 { if idx > 0 {
std::thread::sleep(std::time::Duration::from_millis(PER_CERT_BACKOFF_MS)); std::thread::sleep(std::time::Duration::from_millis(PER_CERT_BACKOFF_MS));
} }
match aacs::handshake::aacs_authenticate(session, &hc.private_key, &hc.certificate) { match aacs::handshake::aacs_authenticate(
session.scsi_mut(),
&hc.private_key,
&hc.certificate,
) {
Ok(mut auth) => { Ok(mut auth) => {
let volume_id = match aacs::handshake::read_volume_id(session, &mut auth) { let volume_id =
match aacs::handshake::read_volume_id(session.scsi_mut(), &mut auth) {
Ok(vid) => vid, Ok(vid) => vid,
Err(e) => { Err(e) => {
tracing::warn!( tracing::warn!(
@@ -134,7 +139,8 @@ impl AacsCertUnlocker<'_> {
} }
}; };
let (read_data_key, read_data_key_err) = match aacs::handshake::read_data_keys( let (read_data_key, read_data_key_err) = match aacs::handshake::read_data_keys(
session, &mut auth, session.scsi_mut(),
&mut auth,
) { ) {
Ok((rdk, _)) => (Some(rdk), None), Ok((rdk, _)) => (Some(rdk), None),
Err(e) => { Err(e) => {