Matthew Jackson eaa158c379 Wire the AACS 2.0 (P-256) host-cert handshake: live and cert-agnostic
The 2.0 host-cert handshake is now live and cert-agnostic (works when a
cert is supplied), with the same chain-of-trust hardening as 1.0; the
SECURITY note is resolved by fixing, not by leaving dead.

- Remove the module-level #[allow(dead_code)]; the P-256/v2 AKE is now
  reachable from run_cert_handshake.
- run_cert_handshake selects the P-256 AKE per host cert: the 1.0 cert is
  tried first (an AACS 2.0 drive accepts it for the VID); if the drive
  refuses it AND the supplied HostCert carries AACS 2.0 (P-256)
  credentials, fall back to the native P-256 AKE. No possession/feature
  gate — it consumes whatever the HostCert carries, exactly as the 1.0
  path consumes hc.certificate. A 1.0-attempt transport fault
  short-circuits the fallback so a dead bus is not hammered.
- Chain-of-trust hardening on the now-live v2 path, mirroring 1.0:
  drive-cert TYPE gate (only 0x11 accepted, else rejected), cert verify
  is FATAL (verify_cert_p256 under the real 2.0 LA anchor), ECDSA-P256
  point validation on every attacker-supplied point (point_on_curve in
  ecdsa_verify_p256 + compute_bus_key_p256), and AGID release on every
  exit.
- The 2.0 cert byte layout/signed-range is documented as PROVISIONAL: no
  genuine 2.0 host/drive AKE cert exists to pin it, and the draft spec
  implies extra Host-ID/paired-DKS fields. A wrong signed range fails
  CLOSED (fatal verify rejects), never trusts. An #[ignore] slot awaits a
  real cert.
- Tests: synthetic test-LA-signed 2.0 certs prove accept + bus-key
  derivation, bad-signature fatal reject, unknown-type reject, and
  off-curve/identity rejection (each red-before-green per guard); a
  genuine AACS 2.0 content cert verifies under the published CC key,
  proving the P-256/SHA-256 primitive against real material.
2026-08-18 23:21:15 -07:00
2026-08-15 08:14:31 -07:00
2026-07-10 12:31:19 -07:00
2026-07-28 15:35:19 -07:00

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: MIT.

S
Description
No description provided
Readme MIT
1.3 MiB
Languages
Rust 100%