test: round-3 coverage on platform shim, ld/aacs error paths, css/mt1959 branches

Targets the files round-1/2 left out of scope: ld/platform/mod.rs (the
PlatformDriver default trait method, previously 0%), ld/error.rs Display
impl (now 100%), aacs/mod.rs Default + full unlock_bus success path
(exposes handshake::tests::DriveEmu/dummy_cert as pub(crate) for reuse),
css/mod.rs (DvdUnlocker::default, the disc-key-refusal non-fatal path,
drive-challenge/Key2-send failure branches), aacs/handshake.rs
(run_cert_handshake's per-cert backoff sleep + retry), and mt1959
mod.rs/variant_a.rs (MODE_B dispatch, a non-transport pass-1 probe
sense, WRITE_BUFFER's 24-bit length guard, variant_a's full upload+
verify+double-unlock success path).

Total coverage 95.65% -> 96.50% (regions), 95.33% -> 96.61% (lines).
This commit is contained in:
Matthew Jackson
2026-08-25 22:13:19 -07:00
parent 7ea1a4eea7
commit 9ad1eb9811
7 changed files with 428 additions and 5 deletions
+28 -5
View File
@@ -1635,7 +1635,7 @@ pub fn run_cert_handshake(
// ── Tests ───────────────────────────────────────────────────────────────────
#[cfg(test)]
mod tests {
pub(crate) mod tests {
use super::*;
use crate::scsi::mock::{MockTransport, Reply};
@@ -1649,7 +1649,7 @@ mod tests {
/// A host cert good enough to reach the SCSI steps (the crypto is exercised
/// by the math tests; these tests are about the transport contract).
fn dummy_cert() -> crate::HostCert {
pub(crate) fn dummy_cert() -> crate::HostCert {
crate::HostCert {
private_key: [0x11u8; 20],
certificate: vec![0u8; 92],
@@ -1749,6 +1749,29 @@ mod tests {
assert_eq!(err, crate::UnlockError::HandshakeRejected);
}
/// A drive that rejects the FIRST cert (a plain cert-level rejection, not a
/// wedge or a dead bus) must roll on to the second cert — and the roll-on
/// is gated by the per-cert backoff sleep (`idx > 0`), which fires only on
/// the second and later attempts. Pins both: the loop actually retries
/// with a second host cert (not just re-reporting the first failure), and
/// the backoff genuinely elapses real time (catches deleting the sleep or
/// gating it on the wrong condition).
#[test]
fn a_rejected_first_cert_backs_off_and_tries_the_second() {
let mut t = MockTransport::always(Reply::illegal_request());
let certs = vec![dummy_cert(), dummy_cert()];
let started = std::time::Instant::now();
let err = run_cert_handshake(&mut t, &certs).expect_err("both certs rejected");
assert_eq!(err, crate::UnlockError::HandshakeRejected);
assert!(
started.elapsed() >= std::time::Duration::from_millis(900),
"must have slept the per-cert backoff before the second attempt"
);
// Each attempt issues 4 invalidations + 1 AGID alloc (which is where
// the rejection fires) = 5 CDBs; two attempts = 10.
assert_eq!(t.calls(), 10, "must have actually issued a second attempt");
}
/// Script a full, successful AACS 1.0 mutual auth against the mock.
///
/// The drive presents a type-0x11 (AACS 2.0) certificate, which the
@@ -1895,7 +1918,7 @@ mod tests {
/// answer the Volume ID read with a MAC that actually verifies — the only
/// way to drive `run_cert_handshake` past the VID gate and into the
/// read-data-keys arm with the crate's static mocks unable to.
struct DriveEmu {
pub(crate) struct DriveEmu {
drive_priv: [u8; 20],
drive_x: [u8; 20],
drive_y: [u8; 20],
@@ -1904,11 +1927,11 @@ mod tests {
/// When set, format-0x84 (read-data-keys) is served a genuine non-zero
/// response instead of the default dead-bus behaviour, letting
/// `run_cert_handshake` run all the way to its `Ok` return.
serve_data_keys: bool,
pub(crate) serve_data_keys: bool,
}
impl DriveEmu {
fn new() -> Self {
pub(crate) fn new() -> Self {
let (drive_priv, drive_x, drive_y) = generate_host_key_pair();
DriveEmu {
drive_priv,
+36
View File
@@ -140,6 +140,42 @@ mod tests {
}
}
/// `Default` must delegate to `new()` (there is only one way to build an
/// `AacsCert`, so this pins the two constructors never drift apart).
#[test]
#[allow(clippy::default_constructed_unit_structs)]
fn default_matches_new() {
let _ = AacsCert::default();
let _ = AacsCert::new();
}
/// THE full success path through `Unlocker::unlock_bus` itself: a
/// self-consistent AACS 1.0 drive emulator carries auth + VID + data-key
/// reads all the way to `Ok(Unlocked { .. })`, so the crate's public entry
/// point — not just `run_cert_handshake` underneath it — is proven to wire
/// `volume_id` and `read_data_key` into `Unlocked` correctly.
#[test]
fn unlock_bus_succeeds_end_to_end() {
let mut t = handshake::tests::DriveEmu::new();
t.serve_data_keys = true;
let id = id();
let certs = [host_cert()];
let ctx = UnlockCtx::new(&id, DiscKind::Aacs, &certs);
let unlocked = AacsCert::new()
.unlock_bus(&mut t, &ctx)
.expect("auth + VID + data-key reads all succeed");
assert_eq!(unlocked.vid, Some([0x5Au8; 16]));
assert!(unlocked.bus_key.is_some());
assert!(
!unlocked.drive_unlocked,
"cert path never sets drive_unlocked"
);
}
fn host_cert() -> crate::HostCert {
handshake::tests::dummy_cert()
}
/// With no host certs there is nothing to authenticate with → NoUsableHostCert,
/// and the transport is never touched.
#[test]
+108
View File
@@ -1021,6 +1021,15 @@ mod tests {
assert_eq!(DvdUnlocker::new().name(), "DVD");
}
/// `Default` must delegate to `new()` — there is only one way to build a
/// `DvdUnlocker` (it is a unit struct), so this pins the two never drift.
#[test]
#[allow(clippy::default_constructed_unit_structs)]
fn default_matches_new() {
let _ = DvdUnlocker::default();
let _ = DvdUnlocker::new();
}
/// DvdUnlocker provides bus removal only — it never provides drive features.
#[test]
fn dvd_unlocker_provides_no_features() {
@@ -1270,6 +1279,70 @@ mod tests {
);
}
/// Host challenge + Key1 both succeed, but the drive challenge REPORT KEY
/// (step 3) is refused. Distinct from `key1_report_failure_fails_the_auth`
/// (step 2) and `key2_send_failure_fails_the_auth` (step 4) below — each
/// pins a different `css_scsi` call site's `?`.
#[test]
fn drive_challenge_report_failure_fails_the_auth() {
struct FailAtDriveChallenge(FakeDvdDrive);
impl ScsiTransport for FailAtDriveChallenge {
fn execute(
&mut self,
cdb: &[u8],
dir: DataDirection,
data: &mut [u8],
timeout_ms: u32,
) -> crate::scsi::Result<crate::scsi::ScsiResult> {
if cdb[0] == crate::scsi::SCSI_REPORT_KEY && cdb[10] & 0x3F == 0x01 {
return Ok(crate::scsi::ScsiResult {
status: 0x02,
bytes_transferred: 0,
sense: [0u8; 32],
});
}
self.0.execute(cdb, dir, data, timeout_ms)
}
}
let mut t = FailAtDriveChallenge(FakeDvdDrive {
variant: 4,
host_challenge: [0u8; 10],
});
let e = authenticate_with_agid(&mut t, 0).expect_err("refused drive challenge");
assert!(matches!(e, Error::CssAuthFailed));
}
/// Host challenge, Key1, and the drive challenge all succeed, but the
/// Key2 SEND KEY (step 4, the last command in the handshake) is refused.
#[test]
fn key2_send_failure_fails_the_auth() {
struct FailAtKey2Send(FakeDvdDrive);
impl ScsiTransport for FailAtKey2Send {
fn execute(
&mut self,
cdb: &[u8],
dir: DataDirection,
data: &mut [u8],
timeout_ms: u32,
) -> crate::scsi::Result<crate::scsi::ScsiResult> {
if cdb[0] == crate::scsi::SCSI_SEND_KEY && cdb[10] & 0x3F == 0x03 {
return Ok(crate::scsi::ScsiResult {
status: 0x02,
bytes_transferred: 0,
sense: [0u8; 32],
});
}
self.0.execute(cdb, dir, data, timeout_ms)
}
}
let mut t = FailAtKey2Send(FakeDvdDrive {
variant: 4,
host_challenge: [0u8; 10],
});
let e = authenticate_with_agid(&mut t, 0).expect_err("refused Key2 send");
assert!(matches!(e, Error::CssAuthFailed));
}
// ── read_disc_key ────────────────────────────────────────────────────────
/// The best-effort disc-key REPORT KEY is refused — `read_disc_key` must
@@ -1422,6 +1495,41 @@ mod tests {
unlock_css_reads(&mut t, 0).expect("css bus-auth + best-effort disc key succeed");
}
/// A drive whose bus-auth succeeds but refuses the best-effort disc-key
/// REPORT KEY (CHECK CONDITION). `unlock_css_reads_inner` must swallow
/// that failure — the read barrier is already open from bus-auth, so the
/// whole unlock must still report `Ok`, not propagate the disc-key error.
struct DiscKeyRefusingDrive(FakeDvdDrive);
impl ScsiTransport for DiscKeyRefusingDrive {
fn execute(
&mut self,
cdb: &[u8],
dir: DataDirection,
data: &mut [u8],
timeout_ms: u32,
) -> crate::scsi::Result<crate::scsi::ScsiResult> {
if cdb[0] == crate::scsi::SCSI_READ_DISC_STRUCTURE {
return Ok(crate::scsi::ScsiResult {
status: 0x02,
bytes_transferred: 0,
sense: [0u8; 32],
});
}
self.0.execute(cdb, dir, data, timeout_ms)
}
}
#[test]
fn disc_key_refusal_is_non_fatal_to_the_overall_unlock() {
let mut t = DiscKeyRefusingDrive(FakeDvdDrive {
variant: 5,
host_challenge: [0u8; 10],
});
unlock_css_reads(&mut t, 0)
.expect("bus-auth succeeded; a refused best-effort disc-key read must not fail it");
}
/// `crypt_key`'s `key_type == 2` arm (`PERM_VARIANT[1]`) has no
/// production caller (only key_type 0, the variant brute-force, and 1,
/// the drive-challenge Key2, are ever invoked from `authenticate_with_agid`)
+25
View File
@@ -136,6 +136,31 @@ mod tests {
}
}
/// `Display` is what shows up in logs — every variant must render without
/// panicking and the SCSI variant must carry the opcode/status through.
#[test]
fn display_renders_every_variant() {
assert_eq!(Error::ProfileParse.to_string(), "drive profile parse error");
assert_eq!(Error::UnlockFailed.to_string(), "firmware unlock failed");
assert_eq!(
Error::SignatureMismatch {
expected: [0; 4],
got: [1; 4],
}
.to_string(),
"signature mismatch"
);
assert_eq!(
Error::Scsi {
opcode: 0x3C,
status: 0x02,
sense: None,
}
.to_string(),
"SCSI error (opcode 0x3c, status 0x02)"
);
}
/// `From<ScsiError>` must carry the status AND sense across the seam —
/// collapsing either one destroys the transport-vs-rejection distinction the
/// whole abort/fall-through split rests on.
+86
View File
@@ -397,6 +397,15 @@ mod tests {
assert_eq!(got, None);
}
/// Public catalog accessors: `profiles()` returns the bundled catalog and
/// `profile()` is `profiles().and_then(get)` for a known drive.
#[test]
fn public_catalog_accessors_find_the_bundled_fixture_drive() {
assert!(super::profiles().is_some(), "bundled catalog must parse");
let m = super::profile(&known_vid_drive_id()).expect("known fixture drive matches");
assert!(m.profile.read_vid_cdb.is_some());
}
/// `unlock_features` on a drive with no matching profile → `NotApplicable`
/// (fall through), short-circuiting before any firmware handshake.
#[test]
@@ -525,6 +534,83 @@ mod tests {
assert_eq!(err, UnlockError::Transport);
}
/// A drive-sense (not a dead bus) rejecting the speed probe is a genuine
/// calibration miss, not a transport fault — `firmware_unlock` must
/// warn-and-continue on the drive's default speed table, still reporting a
/// full unlock. The non-transport sibling of
/// `transport_fault_during_probe_disc_is_transport_not_a_successful_unlock`.
#[test]
fn drive_sense_during_probe_disc_still_reports_a_successful_unlock() {
let id = known_vid_drive_id();
let sig = profile::find_bundled(&id)
.expect("profile")
.profile
.signature;
let mut resp = vec![0u8; 64];
resp[0..4].copy_from_slice(&sig);
resp[12..16].copy_from_slice(&[0x4D, 0x4D, 0x6B, 0x76]); // primary marker
resp[16..20].copy_from_slice(&[0x4C, 0x62, 0x44, 0x72]); // secondary marker
struct ProbeSenseDrive {
resp: Vec<u8>,
}
impl ScsiTransport for ProbeSenseDrive {
fn execute(
&mut self,
cdb: &[u8],
_dir: DataDirection,
data: &mut [u8],
_timeout_ms: u32,
) -> crate::scsi::Result<ScsiResult> {
// READ_BUFFER (0x3C) / SUB_CMD_PROBE (0x14) is the speed probe.
if cdb.first() == Some(&0x3C) && cdb.get(3) == Some(&0x14) {
return Ok(ScsiResult {
status: 0x02,
bytes_transferred: 0,
sense: [0u8; 32],
});
}
let n = self.resp.len().min(data.len());
data[..n].copy_from_slice(&self.resp[..n]);
Ok(ScsiResult {
status: 0,
bytes_transferred: n,
sense: [0u8; 32],
})
}
}
let mut t = ProbeSenseDrive { resp };
let u = LibreDrive::new()
.unlock_features(&mut t, &ctx(&id))
.expect("a calibration miss must not fail the whole unlock");
assert!(u.drive_unlocked);
}
/// `unlock_bus` is the same firmware code as `unlock_features` (bus removal
/// comes free with the drive unlock) — pins that the trait method actually
/// delegates rather than being an accidental no-op stub.
#[test]
fn unlock_bus_delegates_to_the_same_firmware_unlock() {
use crate::scsi::mock::{MockTransport, Reply};
let id = known_vid_drive_id();
let sig = profile::find_bundled(&id)
.expect("profile")
.profile
.signature;
let mut resp = vec![0u8; 64];
resp[0..4].copy_from_slice(&sig);
resp[12..16].copy_from_slice(&[0x4D, 0x4D, 0x6B, 0x76]);
resp[16..20].copy_from_slice(&[0x4C, 0x62, 0x44, 0x72]);
let mut t = MockTransport::always(Reply::good(resp));
let u = LibreDrive::new()
.unlock_bus(&mut t, &ctx(&id))
.expect("unlock_bus runs the same firmware handshake");
assert!(u.drive_unlocked);
}
/// Catches classifying a dead bus as "not this unlocker's drive": the very
/// first unlock command faulting at the transport layer must abort the
/// consumer (`Transport`), not fall through to the next unlocker.
+38
View File
@@ -25,3 +25,41 @@ pub(crate) trait PlatformDriver: Send {
false
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::scsi::mock::{MockTransport, Reply};
/// A minimal driver that takes every `PlatformDriver` default (`is_unlocked`)
/// so the shim's dispatch and the default's `false` are both exercised —
/// every real driver (`Mt1959`) overrides `is_unlocked`, so without this the
/// default body is dead in coverage even though it is reachable code.
struct StubDriver;
impl PlatformDriver for StubDriver {
fn init(&mut self, scsi: &mut dyn ScsiTransport) -> Result<()> {
scsi.execute(&[0], crate::scsi::DataDirection::None, &mut [], 0)?;
Ok(())
}
fn probe_disc(&mut self, scsi: &mut dyn ScsiTransport) -> Result<()> {
scsi.execute(&[0], crate::scsi::DataDirection::None, &mut [], 0)?;
Ok(())
}
fn is_ready(&self) -> bool {
true
}
}
#[test]
fn default_is_unlocked_is_false_and_trait_methods_dispatch() {
let mut drv = StubDriver;
let mut scsi = MockTransport::always(Reply::good(vec![]));
assert!(drv.init(&mut scsi).is_ok());
assert!(drv.probe_disc(&mut scsi).is_ok());
assert!(drv.is_ready());
assert!(!drv.is_unlocked());
}
}
+107
View File
@@ -823,6 +823,49 @@ mod tests {
}
}
/// A drive SENSE (not a dead bus) during the coarse speed probe is the
/// drive's OWN failure, not a transport fault — `run_probe` must report
/// `UnlockFailed`, not `Transport`. The sibling of
/// `transport_fault_during_pass1_probe_stays_a_transport_failure`, which
/// only exercises the `is_transport_failure()` arm.
#[test]
fn drive_sense_during_pass1_probe_is_unlock_failed_not_transport() {
struct SenseAtFirstProbe;
impl ScsiTransport for SenseAtFirstProbe {
fn execute(
&mut self,
cdb: &[u8],
_dir: DataDirection,
data: &mut [u8],
_timeout_ms: u32,
) -> crate::scsi::Result<ScsiResult> {
if cdb.first() == Some(&SCSI_READ_BUFFER) && cdb.get(3) == Some(&SUB_CMD_PROBE) {
return Ok(ScsiResult {
status: 0x02,
bytes_transferred: 0,
sense: [0u8; 32],
});
}
for b in data.iter_mut() {
*b = 0;
}
Ok(ScsiResult {
status: 0,
bytes_transferred: data.len(),
sense: [0u8; 32],
})
}
}
let mut t = SenseAtFirstProbe;
let mut mt = Mt1959::new(fixture_profile([0; 4]), false);
mt.init_complete = true;
let e = mt
.run_probe(&mut t)
.expect_err("drive-refused pass-1 probe");
assert!(!e.is_transport_failure(), "a sense is not a dead bus");
assert!(matches!(e, Error::UnlockFailed));
}
/// THE probe pass-1 dead-bus test. A bus that dies during the coarse speed
/// probe must keep its transport classification out of `run_probe` so the
/// caller (`firmware_unlock`) aborts with `Transport` rather than reporting a
@@ -917,6 +960,48 @@ mod tests {
);
}
/// A firmware blob larger than WRITE_BUFFER's 24-bit CDB length field
/// cannot be uploaded — encoding it would silently disagree with the
/// bytes actually sent. Must reject before issuing any CDB.
#[test]
fn variant_a_refuses_firmware_over_the_24bit_write_buffer_length() {
let mut profile = fixture_profile([0; 4]);
profile.firmware = vec![0u8; 0x0100_0000]; // one past WRITE_BUFFER_MAX_LEN
let mut mt = Mt1959::new(profile, false);
let mut t = RecordingTransport { cdbs: Vec::new() };
let e = variant_a::load_firmware(&mut mt, &mut t).expect_err("blob too large to encode");
assert!(matches!(e, Error::UnlockFailed));
assert!(t.cdbs.is_empty(), "no CDB may reach the drive");
}
/// THE full success path: WRITE_BUFFER upload, a fully-good verify read
/// (exercising the `Ok(r) if status==0 && bytes_transferred==len` arm no
/// other variant_a test reaches), then BOTH `do_unlock` calls succeed —
/// the first fatally-gated, the second best-effort. Every other
/// variant_a test above deliberately stops short (empty firmware, a
/// dead bus, or `do_unlock` failing against `RecordingTransport`'s empty
/// response) — this is the only one that reaches `load_firmware`'s final
/// `Ok(())`.
#[test]
fn variant_a_load_firmware_succeeds_end_to_end() {
let sig = [0x11, 0x22, 0x33, 0x44];
let mut profile = fixture_profile(sig);
profile.firmware = vec![0u8; 64];
let mut mt = Mt1959::new(profile, false);
let unlock_response = build_response(sig, FIRMWARE_ACTIVE_SIG, FIRMWARE_MODE_SIG);
let mut t = crate::scsi::mock::MockTransport::scripted(
vec![
crate::scsi::mock::Reply::good(vec![]), // WRITE_BUFFER upload
crate::scsi::mock::Reply::good(vec![0u8; VALIDATE_RESPONSE_SIZE as usize]), // verify read, full
crate::scsi::mock::Reply::good(unlock_response.clone()), // do_unlock #1
crate::scsi::mock::Reply::good(unlock_response), // do_unlock #2 (best-effort)
],
crate::scsi::mock::Reply::TransportFault,
);
variant_a::load_firmware(&mut mt, &mut t).expect("upload + verify + double unlock all ok");
assert!(mt.init_complete, "do_unlock #1 must have set init_complete");
}
/// variant_b had no transport-abort test. A firmware-upload STEP that hits a
/// dead bus (here the metadata read) must abort via `trace_step`, not be
/// swallowed and carried into the unlock retries.
@@ -1083,6 +1168,28 @@ mod tests {
);
}
/// Same shape as `run_init_exhausts_after_three_generic_failures`, but for
/// a MODE_B (`is_variant_b = true`) drive — pins that `run_init`'s mode
/// dispatch (`self.mode == MODE_A`) actually reaches `variant_b::load_firmware`
/// and not just its `variant_a` sibling, which every other `run_init` test
/// above exercises exclusively.
#[test]
fn run_init_exhausts_after_three_generic_failures_mode_b() {
let mut t = crate::scsi::mock::MockTransport::always(crate::scsi::mock::Reply::short(
vec![0u8; 64],
10,
));
let mut mt = Mt1959::new(fixture_profile([0; 4]), true);
let e = mt.init(&mut t).expect_err("never validates, never uploads");
assert!(matches!(e, Error::UnlockFailed));
assert!(!e.is_transport_failure());
assert_eq!(
t.calls(),
3,
"one do_unlock call per attempt, three attempts, no firmware CDBs"
);
}
/// Same generic `do_unlock` failure, but this time the firmware-reload
/// fallback itself hits a dead bus. That must abort `run_init`
/// immediately (transport classification preserved), not be folded into