mirror of
https://github.com/freemkv/freemkv-keysources.git
synced 2026-10-07 18:13:11 +00:00
log disc-side read failures; skip a bogus CPS unit 0
Two disc-side reads were silently swallowed with unwrap_or(&[]): a failure reading the disc's encrypted title keys or its MKB looked identical to a disc that genuinely had none, hiding real I/O/parse errors from the operator. Log a warning (mirroring online.rs's existing pattern) before falling back to empty in both cases. Also, a stored unit key numbered 0 (an invalid CPS unit number; valid numbering is >= 1) was mapped via num.saturating_sub(1) onto idx 0, silently colliding with and shadowing a legitimate unit 1. Skip unit number 0 instead of mis-mapping it, and add a regression test.
This commit is contained in:
+48
-3
@@ -362,13 +362,30 @@ impl KeydbSource {
|
||||
// 1. Terminal Unit Keys stored in the entry — directly usable, no
|
||||
// derivation. Preserve the keydb's CPS numbering (idx = num - 1).
|
||||
for (num, key) in &entry.unit_keys {
|
||||
keys.push(UnitKey::new(num.saturating_sub(1), *key));
|
||||
// A valid CPS unit number is >= 1; num 0 would collide with unit 1
|
||||
// at idx 0 (num - 1), so skip it rather than mis-map two units.
|
||||
if *num == 0 {
|
||||
continue;
|
||||
}
|
||||
keys.push(UnitKey::new(num - 1, *key));
|
||||
}
|
||||
|
||||
// The disc's encrypted title keys (from Unit_Key_RO.inf). Empty when
|
||||
// the scan captured none, in which case only the stored list (1)
|
||||
// contributes.
|
||||
let enc_title_keys = ctx.enc_title_keys().unwrap_or(&[]);
|
||||
let enc_title_keys = match ctx.enc_title_keys() {
|
||||
Ok(k) => k,
|
||||
Err(e) => {
|
||||
// Mirror online.rs: surface the read failure, then fall back to
|
||||
// empty (only the stored unit-key list can contribute).
|
||||
tracing::warn!(
|
||||
target: "freemkv::keysource",
|
||||
error = %e,
|
||||
"keydb: disc encrypted title keys unreadable; deriving without them"
|
||||
);
|
||||
&[]
|
||||
}
|
||||
};
|
||||
if !enc_title_keys.is_empty() {
|
||||
// VUK path, else MK path (stored/PK/DK) → VUK. See
|
||||
// docs/keydb.md#unit-keys-vuk-or-mk for the VID rules.
|
||||
@@ -376,7 +393,19 @@ impl KeydbSource {
|
||||
uks_from_vuk(&vuk, enc_title_keys)
|
||||
} else {
|
||||
let vid = ctx.vid().or_else(|| entry.vid.map(Vid));
|
||||
let mkb = ctx.mkb().unwrap_or(&[]);
|
||||
let mkb = match ctx.mkb() {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
// Mirror online.rs: log the failure, then fall back to
|
||||
// empty (PK/DK derivation may then find no Media Key).
|
||||
tracing::warn!(
|
||||
target: "freemkv::keysource",
|
||||
error = %e,
|
||||
"keydb: disc MKB unreadable; media-key derivation may fail"
|
||||
);
|
||||
&[]
|
||||
}
|
||||
};
|
||||
let mk: Option<MediaKey> = entry
|
||||
.media_key
|
||||
.map(MediaKey)
|
||||
@@ -672,6 +701,22 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
// A CPS unit number of 0 is invalid (valid numbering is >= 1); it must be
|
||||
// skipped, not mapped to idx 0 where `num - 1` would collide with unit 1.
|
||||
#[test]
|
||||
fn stored_unit_key_with_cps_number_zero_is_skipped() {
|
||||
let mut e = blank_entry(HASH);
|
||||
e.unit_keys = vec![(0, [0xEEu8; 16]), (1, [0xA0u8; 16])];
|
||||
let db = db_with(e, Vec::new());
|
||||
let got = KeydbSource::unit_keys_from(&db, &ctx(HASH, Vec::new(), None));
|
||||
let keys: Vec<[u8; 16]> = got.iter().map(|u| u.key).collect();
|
||||
assert!(
|
||||
!keys.contains(&[0xEEu8; 16]),
|
||||
"a unit-0 key must be dropped, never mapped onto idx 0"
|
||||
);
|
||||
assert_eq!(committed(&got), vec![(1u32, [0xA0u8; 16])]);
|
||||
}
|
||||
|
||||
// Orphan-unit completeness (the real keydb bug): a PARTIAL stored list
|
||||
// plus a VUK that boils BOTH declared units must return BOTH, not shadow
|
||||
// the VUK with the partial list. See docs/keydb.md#test-orphan-union.
|
||||
|
||||
Reference in New Issue
Block a user