css: CSS bus-auth becomes a uniform registry Unlocker

Convert the CSS read-unlock into a first-class registry Unlocker (CssUnlocker)
dispatched through route_unlock like every other barrier removal, instead of
a direct call in scan. libfreemkv appends the built-in CSS unlocker (and, next,
the AACS cert handshake) exactly once via ensure_builtins(), AFTER any
client-registered firmware unlocker — so the registry order is firmware → cert
→ css, owned by the lib, not the client.

Defense in depth: the unlocker does NOT trust the caller-declared DiscKind.
matches() filters on the declared kind (Css), but unlock() self-verifies
against the drive's GET CONFIGURATION profile and refuses (UnlockError::
NotApplicable, a new shared "this unlocker doesn't apply" variant) WITHOUT
issuing a single CSS CDB if the drive reports a non-DVD profile — so a
mis-routed Blu-ray is never sent CSS bus-auth. Guard the firmware unlocker the
same structural way (it matches only the drive-prep phase, kind == Unknown).

Tests: CssUnlocker matches only DiscKind::Css; a BD-profile drive yields
NotApplicable with zero CSS CDBs issued.
This commit is contained in:
Matthew Jackson
2026-06-29 16:49:52 -07:00
parent cfcc524367
commit bac105a022
4 changed files with 194 additions and 10 deletions
+4 -1
View File
@@ -221,6 +221,7 @@ fn unlock_error_to_error(e: crate::unlock::UnlockError) -> Error {
UnlockError::HandshakeRejected
| UnlockError::CertRevoked { .. }
| UnlockError::FirmwareNotUnlockable
| UnlockError::NotApplicable
| UnlockError::Scsi(_) => Error::AacsHostCertRejected,
}
}
@@ -235,7 +236,9 @@ fn cert_unlock_outcome(e: &crate::unlock::UnlockError) -> crate::aacs::UnlockOut
UnlockError::NoUsableHostCert { mkb } => UnlockOutcome::NoUsableHostCert { mkb: *mkb },
UnlockError::CertRevoked { mkb } => UnlockOutcome::CertRevoked { mkb: *mkb },
UnlockError::VidUnavailable => UnlockOutcome::VidUnavailable,
UnlockError::HandshakeRejected | UnlockError::Scsi(_) => UnlockOutcome::HandshakeRejected,
UnlockError::HandshakeRejected | UnlockError::NotApplicable | UnlockError::Scsi(_) => {
UnlockOutcome::HandshakeRejected
}
}
}