AACS: DiscInputs gains app-populated samples for ciphertext-validating sources
An online key service validates a candidate against real ciphertext, so it needs a few encrypted content sample units. Add a samples field to DiscInputs; Disc::inputs() leaves it empty (reading content needs the disc reader, which scan does not retain) for the application to fill.
This commit is contained in:
+8
-5
@@ -1492,11 +1492,11 @@ impl Disc {
|
||||
/// AACS-chain steps it can from disc-read inputs (MKB / VID / `Unit_Key_RO.inf`).
|
||||
///
|
||||
/// `#[non_exhaustive]`: AACS is a derivation chain
|
||||
/// (`DK →(MKB)→ MK →(VID)→ VK →(Unit_Key_RO)→ UK`). Today only the final
|
||||
/// [`Key::Unit`] entry point is wired; the higher entry points
|
||||
/// (device / media / volume key) land as the derivation is lifted out of the
|
||||
/// keydb-coupled scan path. Adding them is non-breaking thanks to
|
||||
/// `#[non_exhaustive]`.
|
||||
/// (`DK →(MKB)→ MK →(VID)→ VK →(Unit_Key_RO)→ UK`). Each variant is an entry
|
||||
/// point at one level of that chain; [`Disc::decrypt_with`] derives down from
|
||||
/// it to the per-CPS unit keys. New levels can be added without breaking
|
||||
/// callers.
|
||||
#[derive(Debug, Clone)]
|
||||
#[non_exhaustive]
|
||||
pub enum Key {
|
||||
/// Device key(s) (AACS DK, positioned). libfreemkv walks the MKB
|
||||
@@ -1602,6 +1602,9 @@ impl Disc {
|
||||
volume_id: a.volume_id,
|
||||
mkb: a.mkb.clone(),
|
||||
unit_key_ro: a.uk_ro.clone(),
|
||||
// Content samples need the disc reader, which scan does not retain;
|
||||
// the caller fills these for sources that validate against ciphertext.
|
||||
samples: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+25
-13
@@ -29,21 +29,33 @@ pub struct DiscInputs {
|
||||
pub mkb: Vec<u8>,
|
||||
/// Raw `Unit_Key_RO.inf` bytes. Empty when not captured.
|
||||
pub unit_key_ro: Vec<u8>,
|
||||
/// Encrypted on-disc content sample units (each a 6144-byte aligned unit),
|
||||
/// for sources that validate a key server-side against real ciphertext
|
||||
/// (e.g. an online key service). Empty for sources that don't need them
|
||||
/// (a local keydb). Populated by the application — reading content requires
|
||||
/// the disc reader, which the library's scan does not retain — so
|
||||
/// [`crate::Disc::inputs`] leaves it empty for the caller to fill.
|
||||
pub samples: Vec<Vec<u8>>,
|
||||
}
|
||||
|
||||
/// A key source: given a disc's [`DiscInputs`], look up a [`Key`].
|
||||
/// A key source: given a disc's [`DiscInputs`], offer candidate [`Key`]s.
|
||||
///
|
||||
/// Dumb by contract — a source queries its backing store and returns the raw
|
||||
/// key at whatever level it has (device / processing / media / volume / unit).
|
||||
/// It performs NO AACS derivation; `Disc::decrypt_with` derives down. That
|
||||
/// keeps every derivation step in one place (the library) across AACS
|
||||
/// 1.0 / 2.0 / 2.1 / 2.x.
|
||||
/// Dumb by contract — a source queries its backing store and enumerates the raw
|
||||
/// material it holds as candidate keys at whatever level it has (device /
|
||||
/// processing / media / volume / unit). It performs NO AACS derivation and NO
|
||||
/// validation; `Disc::decrypt_with` derives down, and the caller validates by
|
||||
/// decrypting a sample. That keeps every derivation step in one place (the
|
||||
/// library) across AACS 1.0 / 2.0 / 2.1 / 2.x.
|
||||
///
|
||||
/// A source returns *multiple ordered candidates* because a single store can
|
||||
/// hold material for several derivation paths (a keydb has a per-disc VUK *and*
|
||||
/// a device-key pool *and* a media-key pool — the source can't know which
|
||||
/// applies without the MKB walk, which is derivation). The caller tries the
|
||||
/// candidates in order and keeps the first that decrypts (validate-before-
|
||||
/// return). A source that resolves server-side (an online key service) or holds
|
||||
/// a cached final key (the mapfile) simply returns one candidate.
|
||||
pub trait KeySource {
|
||||
/// Look up a key for this disc.
|
||||
///
|
||||
/// - `Ok(Some(key))` — a key was found; the caller hands it to
|
||||
/// `Disc::decrypt_with`.
|
||||
/// - `Ok(None)` — this source has nothing for the disc; try the next one.
|
||||
/// - `Err(_)` — the source itself failed (I/O, network, parse).
|
||||
fn resolve(&self, inputs: &DiscInputs) -> Result<Option<Key>>;
|
||||
/// Candidate keys for this disc, most-specific first. Empty = this source
|
||||
/// has nothing; `Err(_)` = the source itself failed (I/O, network, parse).
|
||||
fn resolve(&self, inputs: &DiscInputs) -> Result<Vec<Key>>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user