AACS: decrypt_with accepts higher-level keys and derives down

Disc::decrypt_with now takes Device / Processing / Media / Volume keys in
addition to Unit. A caller hands in whatever level it resolved and the
library derives down the AACS chain to the per-CPS-unit keys, then
decrypts:

  Device   -> MKB walk         -> media key -> VUK -> per-CPS unit keys
  Processing -> MKB            -> media key -> VUK -> per-CPS unit keys
  Media      -> Volume ID      -> VUK              -> per-CPS unit keys
  Volume     -> Unit_Key_RO.inf, one unit key per CPS unit
  Unit       -> used directly (terminal)

Derivation stays centralized in the version-dispatched resolver
(1.0 / 2.0 / 2.1), fed by a single-key provider built from the supplied
key — no new crypto. Volume notably does NOT stop at the volume key: it
decrypts every CPS unit's key.

Scan stashes the AACS inputs (Unit_Key_RO.inf and MKB) on AacsState so an
out-of-band decrypt_with can derive without re-reading the disc.

Non-breaking: Key is #[non_exhaustive] and the existing Unit path is
unchanged. New tests cover the Volume -> per-CPS derive-down, the
missing-inputs error, and the no-units rejection.
This commit is contained in:
MattJackson
2026-06-04 14:20:16 -07:00
parent 8bc1de6c9b
commit 9012101573
3 changed files with 233 additions and 11 deletions
+6 -2
View File
@@ -465,8 +465,12 @@ impl Disc {
unit_keys: resolved.unit_keys,
read_data_key,
volume_id,
uk_ro: Vec::new(),
mkb: Vec::new(),
// Stash the AACS inputs so a later out-of-band `Disc::decrypt_with`
// (caller-resolved Key → derive down) can run without re-reading
// the disc. The borrows in `aacs_ctx` ended when `scheme.load`
// returned, so these buffers are free to move here.
uk_ro: uk_ro_data,
mkb: mkb_data.unwrap_or_default(),
})
}