FMTS: resolve the index key map from one forensic keyserver query
The keyserver protocol now returns all 32 index keys as an array for a forensic content sample (and a single-element array for plain content). resolve_fmts_key_map sends one forensic batch and maps array element i to segment index i+1, replacing the per-index blind-probe collection loop that repeatedly hit the key service. Segment/index parsing and the aligned-unit content classification are reworked to support this: - rename variant_select -> index_select (per-index, not per-variant) - content classification moves to is_clean(buf, ContentFormat) so the unit selector emits only units the key service accepts - segment.rs: parse IndividualSegment.tbl index tags + SPN ranges, build contiguous LBA key ranges from the resolved 32-key array - decrypt/decorator plumbing for the resolved per-index keys Fail loud (FmtsKeyMissing) when the forensic query returns < 32 keys or any segment index stays unresolved.
This commit is contained in:
+28
-2
@@ -318,7 +318,28 @@ pub fn key_fetch(
|
||||
inputs: DiscInputs,
|
||||
make_sources: std::sync::Arc<dyn Fn() -> Vec<Box<dyn KeySource>> + Send + Sync>,
|
||||
) -> crate::sector::KeyFetch {
|
||||
// Memoize by the fingerprint of the sample batch. The resolved keys are
|
||||
// disc-level (the same clip's index / CPS keys are identical for every title
|
||||
// that references it), and this one closure is shared across every title's mux
|
||||
// — so the first title resolves a given batch over the network and every later
|
||||
// title (or repeated batch) is answered from the cache with no request. Empty
|
||||
// replies are cached too: a key the service does not have for a batch will not
|
||||
// appear on a re-ask, so re-hitting the network buys nothing.
|
||||
let cache: std::sync::Arc<std::sync::Mutex<std::collections::HashMap<u64, Vec<[u8; 16]>>>> =
|
||||
std::sync::Arc::new(std::sync::Mutex::new(std::collections::HashMap::new()));
|
||||
std::sync::Arc::new(move |samples: &[Vec<u8>]| -> Vec<[u8; 16]> {
|
||||
let fp = {
|
||||
use std::hash::{Hash, Hasher};
|
||||
let mut h = std::collections::hash_map::DefaultHasher::new();
|
||||
samples.len().hash(&mut h);
|
||||
for s in samples {
|
||||
s.hash(&mut h);
|
||||
}
|
||||
h.finish()
|
||||
};
|
||||
if let Some(hit) = cache.lock().unwrap_or_else(|e| e.into_inner()).get(&fp) {
|
||||
return hit.clone();
|
||||
}
|
||||
let sources = make_sources();
|
||||
let mut di = inputs.clone();
|
||||
di.samples = samples.to_vec();
|
||||
@@ -327,10 +348,15 @@ pub fn key_fetch(
|
||||
// derives unit keys from `enc_title_keys`, which a V10 disc parses at the
|
||||
// 48-byte stride — hardcoding the V20 stride here corrupted them.
|
||||
let ctx = DiscInputsCtx::new(&di);
|
||||
fetch_unit_keys(&sources, &ctx)
|
||||
let keys: Vec<[u8; 16]> = fetch_unit_keys(&sources, &ctx)
|
||||
.into_iter()
|
||||
.map(|u| u.key)
|
||||
.collect()
|
||||
.collect();
|
||||
cache
|
||||
.lock()
|
||||
.unwrap_or_else(|e| e.into_inner())
|
||||
.insert(fp, keys.clone());
|
||||
keys
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user