AACS: own keydb parser + 100% parse + get_uk derivation
- Relocate keydb.cfg parser into keydb_format.rs (libfreemkv no longer knows keydb); add 100% parse (mkb_version/volume_size/is_uhd, revoked_at_mkb) + helper API (get_uk/get_uks/get_vid/host_certs(mkb)). - KeydbSource/OnlineSource/MultiSource -> get_uk(ctx); MultiSource host_certs union; KAT-proven derivation parity. NumberedUnitKey alias. clippy clean.
This commit is contained in:
+40
-30
@@ -1,75 +1,85 @@
|
||||
//! Pluggable AACS key sources for libfreemkv.
|
||||
//!
|
||||
//! libfreemkv performs no key lookup — it is handed a [`Key`] and derives down
|
||||
//! the AACS chain to decrypt. This crate provides the published [`KeySource`]
|
||||
//! implementations that do the lookup:
|
||||
//! libfreemkv owns the AACS crypto; this crate provides the published
|
||||
//! [`KeySource`] implementations that look a disc up and drive the boil-down
|
||||
//! primitives down to terminal Unit Keys:
|
||||
//!
|
||||
//! - [`KeydbSource`] — a local `keydb.cfg` (source #1).
|
||||
//! - [`OnlineSource`] — a remote key service (source #2).
|
||||
//! - [`MapfileSource`] — the persisted unit key from a rip mapfile (source #3).
|
||||
//!
|
||||
//! Applications (autorip, the `freemkv` CLI) choose and order the sources from
|
||||
//! their own config — the local-vs-online policy is just which impls they plug
|
||||
//! in — then resolve and hand the resulting key to `Disc::decrypt_with`.
|
||||
//!
|
||||
//! Sources are dumb and stateful: each hands its candidate keys out one at a
|
||||
//! time via [`KeySource::next_key`], in its own best order, and reports
|
||||
//! exhaustion. Compose several with [`MultiSource`] in the caller's chosen
|
||||
//! order. Resolving those candidates against a disc, and reading the encrypted
|
||||
//! content-sample units a key server validates on, is decryption *mechanism* —
|
||||
//! it lives in the library (`libfreemkv::resolve_and_apply`,
|
||||
//! `libfreemkv::read_encrypted_units`), not here. A source only ever looks a key
|
||||
//! up and hands it back; what's done with the key is not its concern.
|
||||
//! Each source resolves a disc's terminal **Unit Keys** in one shot via
|
||||
//! [`KeySource::get_uk`], driving libfreemkv's boil-down crypto primitives for
|
||||
//! whatever level of material it holds. Compose several with [`MultiSource`] in
|
||||
//! the caller's chosen order. Reading the encrypted content-sample units a key
|
||||
//! server validates on, and applying the resolved keys against a disc, is
|
||||
//! decryption *mechanism* — it lives in the library
|
||||
//! (`libfreemkv::resolve_and_apply`, `libfreemkv::read_encrypted_units`), not
|
||||
//! here.
|
||||
|
||||
mod keydb;
|
||||
mod mapfile;
|
||||
mod keydb_format;
|
||||
mod online;
|
||||
mod paths;
|
||||
|
||||
pub use keydb::KeydbSource;
|
||||
pub use mapfile::MapfileSource;
|
||||
pub use online::{OnlineSource, validate_keyserver_url};
|
||||
pub use paths::{default_keydb_path, existing_keydb_path, keydb_search_paths};
|
||||
|
||||
// Re-exported for downstream convenience so apps need only depend on this crate
|
||||
// for the source-side types.
|
||||
pub use libfreemkv::{DiscInputs, Key, KeySource};
|
||||
pub use libfreemkv::aacs::UnitKey;
|
||||
pub use libfreemkv::keysource::ResolveCtx;
|
||||
pub use libfreemkv::{DiscInputs, KeySource};
|
||||
|
||||
/// An ordered composition of key sources, driven as one. `next_key` exhausts
|
||||
/// the first source (one candidate per call), then the next, … then `None`.
|
||||
/// **The caller supplies the list AND the order** — local-first `[Keydb,
|
||||
/// An ordered composition of key sources, driven as one. [`MultiSource::get_uk`]
|
||||
/// tries each inner source in order and returns the first non-empty Unit Key
|
||||
/// set. **The caller supplies the list AND the order** — local-first `[Keydb,
|
||||
/// Online]`, online-first `[Online, Keydb]`, resume `[Mapfile, Keydb]`, etc. —
|
||||
/// so the "which sources, in what order" policy lives entirely with the
|
||||
/// application, not the library. `MultiSource` is itself a [`KeySource`], so it
|
||||
/// nests and composes.
|
||||
pub struct MultiSource {
|
||||
sources: Vec<Box<dyn KeySource>>,
|
||||
idx: usize,
|
||||
}
|
||||
|
||||
impl MultiSource {
|
||||
/// Compose the given sources, tried in the order supplied.
|
||||
pub fn new(sources: Vec<Box<dyn KeySource>>) -> Self {
|
||||
Self { sources, idx: 0 }
|
||||
Self { sources }
|
||||
}
|
||||
}
|
||||
|
||||
impl KeySource for MultiSource {
|
||||
fn next_key(&mut self, inputs: &DiscInputs) -> Option<Key> {
|
||||
while self.idx < self.sources.len() {
|
||||
if let Some(key) = self.sources[self.idx].next_key(inputs) {
|
||||
return Some(key);
|
||||
/// Try each inner source in order; the FIRST to return a non-empty Unit Key
|
||||
/// set wins. An inner source that returns empty OR errors is treated as "no
|
||||
/// key here" and the next is tried (a single source failure never blocks the
|
||||
/// chain). All sources exhausted → empty.
|
||||
fn get_uk(&self, ctx: &dyn ResolveCtx) -> Result<Vec<UnitKey>, libfreemkv::Error> {
|
||||
for s in &self.sources {
|
||||
if let Ok(uks) = s.get_uk(ctx) {
|
||||
if !uks.is_empty() {
|
||||
return Ok(uks);
|
||||
}
|
||||
}
|
||||
self.idx += 1; // this source is spent — advance to the next
|
||||
}
|
||||
None
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
fn needs_samples(&self) -> bool {
|
||||
self.sources.iter().any(|s| s.needs_samples())
|
||||
/// UNION every inner source's host certs (filtered at the given MKB
|
||||
/// generation). Without this a composed source would hide an inner source's
|
||||
/// cert from the OEM cert-auth route — the gap this fixes.
|
||||
fn host_certs(&self, mkb: Option<u32>) -> Vec<libfreemkv::aacs::HostCert> {
|
||||
self.sources
|
||||
.iter()
|
||||
.flat_map(|s| s.host_certs(mkb))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn errored(&self) -> bool {
|
||||
self.sources.iter().any(|s| s.errored())
|
||||
fn label(&self) -> &'static str {
|
||||
"multi"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user