unlock: dispatch via freemkv-unlock; delete in-tree handshake/css-auth/registry

Rewire the three unlock dispatch points through the freemkv-unlock crate via a
private `unlock_bridge`: drive-prep (kind=Unknown) at `Drive::init`, AACS cert
(kind=Aacs) at `do_handshake_cert`, CSS bus-auth (kind=Css) at scan. The bridge
news up `all_unlockers()` and runs the first matching one, mapping its
`Unlocked` result to the bus-key gate. After a successful drive unlock,
libfreemkv issues a generic SET CD SPEED (max) itself — the old per-unlocker
trait method is gone.

Delete the in-tree unlock code now owned by freemkv-unlock: the AACS cert
handshake (`aacs/handshake.rs`), the CSS bus-auth (`css/auth.rs`), and the
unlock registry (`unlock.rs`). Host-cert collection (a keysource concern) stays
in a small `aacs/host_certs.rs`. No public unlock surface remains — clients
touch libfreemkv only, oblivious to unlockers (as they are to SCSI). 2277 tests
pass.
This commit is contained in:
Matthew Jackson
2026-06-29 20:45:00 -07:00
parent 3bdb6f8b1a
commit 2ba6274eae
12 changed files with 264 additions and 3987 deletions
+22 -33
View File
@@ -1397,7 +1397,7 @@ impl Disc {
tracing::info!(target: "freemkv::scan", handshake = handshake.is_some(), "phase: handshake done");
// Request max read speed — removes riplock on DVD
// (BD/UHD speed is set by firmware init, but DVD needs explicit SET CD SPEED)
// (BD/UHD speed is set by drive unlock/init, but DVD needs explicit SET CD SPEED)
session.set_speed(0xFFFF);
// Read UDF filesystem with buffered sector reader
@@ -1472,38 +1472,27 @@ impl Disc {
if let Some(unlock_lba) = main_extents.first().map(|e| e.start_lba) {
tracing::info!(target: "freemkv::scan", unlock_lba, "phase: CSS — bus-auth unlock");
// Unlock the drive's CSS read gating through the uniform
// unlocker registry: the in-tree CssUnlocker matches
// DiscKind::Css and runs the bus-auth handshake. A CSS-enforcing
// drive (the BU40N) refuses to return scrambled sectors until
// that handshake has run; we run it purely for that unlock and
// IGNORE any key (the descramble key is recovered keylessly from
// the scrambled movie data via the known-plaintext attack — no
// player keys, no disc-key crack, no REPORT-KEY title key).
// unlocker dispatch: the CSS unlocker matches DiscKind::Css and
// runs the bus-auth handshake (self-guarding to DVD media). A
// CSS-enforcing drive (the BU40N) refuses to return scrambled
// sectors until that handshake has run; we run it purely for that
// unlock and IGNORE any key (the descramble key is recovered
// keylessly from the scrambled movie data via the known-plaintext
// attack — no player keys, no disc-key crack, no REPORT-KEY title
// key). Any failure is non-fatal: continue to the crack, which
// simply finds nothing if the drive kept the sectors gated.
let drive_id = session.drive_id.clone();
let css_ctx =
crate::unlock::UnlockCtx::new(&drive_id, crate::unlock::DiscKind::Css);
match crate::unlock::route_unlock(session.scsi_mut(), &css_ctx) {
Ok(crate::unlock::UnlockRoute::Unlocked(..)) => {}
Ok(crate::unlock::UnlockRoute::Failed(e)) => {
tracing::warn!(
target: "freemkv::scan",
outcome = ?e,
"CSS bus-auth unlock failed; scrambled sectors may be unavailable"
);
}
Ok(crate::unlock::UnlockRoute::NoMatch) => {
tracing::warn!(
target: "freemkv::scan",
"no CSS unlocker registered; scrambled sectors may be unavailable"
);
}
Err(e) => {
tracing::warn!(
target: "freemkv::scan",
error_code = e.code(),
"CSS bus-auth unlock hit a transport fault; scrambled sectors may be unavailable"
);
}
if let Err(e) = crate::unlock_bridge::run_unlockers(
session.scsi_mut(),
&drive_id,
freemkv_unlock::DiscKind::Css,
&[],
) {
tracing::warn!(
target: "freemkv::scan",
outcome = ?e,
"CSS bus-auth unlock did not apply; scrambled sectors may be unavailable"
);
}
// Size the crack's batch reads to THIS drive's per-command max
// (DVD ≈ 16; the USB bridge may be lower) — an over-large
@@ -3079,7 +3068,7 @@ impl Disc {
);
// Request the drive's max read speed for the whole sweep — removes
// riplock. BD/UHD get their speed from the firmware unlock/init, but a
// riplock. BD/UHD get their speed from the drive unlock/init, but a
// DVD skips that path (the stock-mode gate, `Drive::disc_is_dvd`), so
// without this explicit SET CD SPEED a DVD rip sweeps at the drive's
// default (riplocked) speed. The damage-recovery branch below also