MEDIUM (coupled):
- aacs/handshake: on the AACS 1.0 path a type-0x11 drive cert is now
REJECTED (verify_cert + step-6 drive-key signature always run) instead
of accepted unverified — the old 0x11-skip did neither yet still ran
ECDH, an attacker-chosen-bus-key hole. run_cert_handshake routes a
genuine 0x11 drive to the native P-256 AKE via the v2-cred fallback.
- aacs/handshake: a post-auth VID/MAC failure (AacsVidMac) with v2 creds
present now falls through to aacs2_authenticate_p256 instead of a
terminal VidUnavailable, so a backward-compat 2.0 drive that fails the
1.0 VID MAC still reaches the P-256 path.
LOW:
- Gate the native AACS 2.0 (P-256) path behind AACS2_P256_EXPERIMENTAL
(default off) at the run_cert_handshake boundary: its cert offsets are
provisional, so production can't silently mis-verify a real disc.
- Release the AGID on the fully-successful handshake too (was leaked,
slowly draining the drive's 4-AGID pool).
- Rejection-sample the ephemeral ECDH private key (drop the modulo bias
of raw % n) in both generate_host_key_pair{,_p256}.
- Guard shared.infinity in compute_bus_key{,_p256} (return None).
- Remove the dead, truncating AacsAuth.drive_cert field.
- Wire UnlockError::NoUsableHostCert for the all-certs-skipped / empty
path (more accurate than HandshakeRejected).
- css: promote crypt_key's debug_assert! bounds checks to real assert!.
Handshake tests reworked onto an anchor-parametrised seam
(run_cert_handshake_with_anchors / *_with_anchor) so the DriveEmu drives
a genuine LA-signed 1.0 AKE under a self-generated test anchor; adds the
0x11-rejection, VID-MAC→P-256 fallback (HybridDrive), AGID-release,
infinity-guard, NoUsableHostCert and crypt_key-assert tests.
freemkv-unlock
The unlock layer for the freemkv toolchain.
An unlocker removes a drive-level bus-encryption barrier so the drive serves readable (de-bus'd / de-scrambled) sectors. Content-key decryption is a separate concern — the consumer's job.
This crate defines the Unlocker contract and a generic SCSI transport
contract, and holds the self-contained unlocker modules. The consumer
(libfreemkv) depends on this crate and
dispatches through all_unlockers(); it never names an individual unlocker, and
clients of libfreemkv are oblivious to unlockers entirely (as they are to the
SCSI layer).
use freemkv_unlock::UnlockError;
// Drive-prep: try each unlocker's feature unlock until one claims the drive.
// `NotApplicable` means "not this unlocker's drive" — move on; a transport
// error means a dead bus — abort. `unlock_bus` follows the same contract for
// removing per-disc bus encryption.
for u in freemkv_unlock::all_unlockers() {
match u.unlock_features(&mut scsi, &ctx) {
Ok(unlocked) => return Ok(unlocked),
Err(UnlockError::NotApplicable) => continue,
Err(e) => return Err(e),
}
}
To remove an unlocker, delete its module directory and its one line in
all_unlockers() — nothing else changes.
License
freemkv-unlock's own source is MIT — see LICENSE.
src/ld/profiles.json bundles volatile, RAM-only LibreDrive microcode
authored and owned by the MakeMKV team (GuinpinSoft inc.) — Copyright (c)
MakeMKV team, all rights reserved. That microcode is third-party proprietary
material, not covered by the MIT license; no ownership is claimed. See
NOTICE. With thanks to the MakeMKV team for
LibreDrive.