AACS: add KeySource trait + DiscInputs; Media key carries a candidate pool
The library now describes the key-source abstraction it consumes, while the concrete sources live in a companion crate. A KeySource looks a disc up from its DiscInputs (disc hash, volume id, MKB, Unit_Key_RO.inf) and returns a Key; the library derives down and decrypts. Sources do no derivation. Disc::inputs() exposes those inputs from a scanned disc. Key::Media now carries a Vec of candidate media keys rather than one: a media key is MKB-scoped, so a source hands its whole pool and the library picks the one that verifies against the disc's MKB. That mirrors Device/Processing and keeps key selection (a derivation step) inside the library.
This commit is contained in:
+23
-5
@@ -1508,9 +1508,12 @@ pub enum Key {
|
||||
/// Processing key(s) (AACS PK). libfreemkv applies each against the MKB
|
||||
/// → media key → VUK → unit keys.
|
||||
Processing(Vec<[u8; 16]>),
|
||||
/// Media key (Km). libfreemkv derives the VUK from it via the Volume ID,
|
||||
/// then the per-CPS-unit keys.
|
||||
Media([u8; 16]),
|
||||
/// Media key candidate(s) (Km). A source hands its full pool because an MK
|
||||
/// is MKB-scoped (shared across a pressing/MKB family) — picking the one
|
||||
/// that applies is `km_verifies` against this disc's MKB, which is
|
||||
/// derivation, so it lives here. libfreemkv verifies, then derives the VUK
|
||||
/// via the Volume ID and the per-CPS-unit keys.
|
||||
Media(Vec<[u8; 16]>),
|
||||
/// Volume Unique Key (VK / VUK). libfreemkv decrypts `Unit_Key_RO.inf`
|
||||
/// into the per-CPS-unit keys. NOT terminal — the chain continues to the
|
||||
/// unit keys.
|
||||
@@ -1589,6 +1592,19 @@ impl Disc {
|
||||
}
|
||||
}
|
||||
|
||||
/// The public AACS inputs for this disc, for a [`crate::KeySource`] to look
|
||||
/// a key up. `None` when the disc carries no AACS state (unencrypted, CSS,
|
||||
/// or AACS inputs not captured at scan). Contains no secrets — just disc
|
||||
/// identity plus the on-disc AACS structures.
|
||||
pub fn inputs(&self) -> Option<crate::keysource::DiscInputs> {
|
||||
self.aacs.as_ref().map(|a| crate::keysource::DiscInputs {
|
||||
disc_hash: a.disc_hash.clone(),
|
||||
volume_id: a.volume_id,
|
||||
mkb: a.mkb.clone(),
|
||||
unit_key_ro: a.uk_ro.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Apply a caller-resolved [`Key`] so [`Self::decrypt_keys`] yields usable
|
||||
/// decryption state. **Lookup-free**: no keydb, no network — the caller
|
||||
/// (an application, via a key source) does all resolution and hands the key
|
||||
@@ -1624,7 +1640,7 @@ impl Disc {
|
||||
match key {
|
||||
Key::Device(dks) => supplied.device_keys = dks,
|
||||
Key::Processing(pks) => supplied.processing_keys = pks,
|
||||
Key::Media(mk) => supplied.media_keys = vec![mk],
|
||||
Key::Media(mks) => supplied.media_keys = mks,
|
||||
Key::Volume(vuk) => {
|
||||
supplied.disc_entry = Some(crate::aacs::DiscEntry {
|
||||
disc_hash: aacs.disc_hash.clone(),
|
||||
@@ -3040,7 +3056,9 @@ mod tests {
|
||||
let mut disc2 = make_test_disc(1000, "UHD");
|
||||
disc2.encrypted = true;
|
||||
assert!(matches!(
|
||||
disc2.decrypt_with(Key::Media([0x22u8; 16])).unwrap_err(),
|
||||
disc2
|
||||
.decrypt_with(Key::Media(vec![[0x22u8; 16]]))
|
||||
.unwrap_err(),
|
||||
crate::error::Error::AacsNoKeys
|
||||
));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
//! Key sources — the lookup layer that hands libfreemkv a [`Key`].
|
||||
//!
|
||||
//! libfreemkv performs NO key lookup. An application resolves a key for a disc
|
||||
//! through one or more [`KeySource`]s, each a dumb adapter over a backing store
|
||||
//! (a keydb file, a key server, the mapfile cache): given the disc's
|
||||
//! [`DiscInputs`] it returns the raw [`Key`] at whatever level it holds. The
|
||||
//! library then derives down and decrypts via `Disc::decrypt_with`.
|
||||
//!
|
||||
//! Source implementations are published in the companion `freemkv-keysources`
|
||||
//! crate — keeping all key *policy* (which store, which order, online vs local)
|
||||
//! out of the library while all key *mechanism* (the AACS derivation chain)
|
||||
//! stays in it.
|
||||
|
||||
use crate::disc::Key;
|
||||
use crate::error::Result;
|
||||
|
||||
/// The public AACS inputs a key source needs to look a disc up. Captured at
|
||||
/// scan; contains no secrets — only the disc identity and the on-disc AACS
|
||||
/// structures a source or key server may key on.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DiscInputs {
|
||||
/// SHA-1 of `Unit_Key_RO.inf`, `0x`-prefixed hex. The value a keydb keys
|
||||
/// its per-disc entries by, and a key server identifies the disc with.
|
||||
pub disc_hash: String,
|
||||
/// Volume ID (16 bytes). `[0u8; 16]` when no authenticated handshake ran
|
||||
/// (e.g. an ISO/mapfile flow), which disables VID-keyed lookups.
|
||||
pub volume_id: [u8; 16],
|
||||
/// Raw MKB bytes. Empty when not captured.
|
||||
pub mkb: Vec<u8>,
|
||||
/// Raw `Unit_Key_RO.inf` bytes. Empty when not captured.
|
||||
pub unit_key_ro: Vec<u8>,
|
||||
}
|
||||
|
||||
/// A key source: given a disc's [`DiscInputs`], look up a [`Key`].
|
||||
///
|
||||
/// 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.
|
||||
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>>;
|
||||
}
|
||||
@@ -85,6 +85,7 @@ pub(crate) mod identity;
|
||||
pub(crate) mod ifo;
|
||||
pub mod io;
|
||||
pub mod keydb;
|
||||
pub mod keysource;
|
||||
pub mod labels;
|
||||
pub(crate) mod mpls;
|
||||
pub mod mux;
|
||||
@@ -174,6 +175,7 @@ pub use disc::{
|
||||
LabelPurpose, LabelQualifier, PatchOptions, PatchOutcome, Resolution, SampleRate, ScanOptions,
|
||||
Stream, SubtitleStream, SweepOptions, VideoStream, classify_damage,
|
||||
};
|
||||
pub use keysource::{DiscInputs, KeySource};
|
||||
|
||||
// ─── Streams ────────────────────────────────────────────────────────────────
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user