Two changes that make AACS 1.0 / DVD self-sufficient: 1. MKB record-type identification bug fix. `mkb_find_mk_dv` was searching for type 0x10 (which is Type-and-Version, 12 bytes) when the Verify Media Key Record is actually type 0x81 for AACS 1.0 or type 0x86 for AACS 2.0/2.1. `mkb_version` had the inverse bug. PK and DK derivation paths therefore silently failed on every disc, masking how often the fallback paths could have worked. Fix searches the correct types; tests added covering both the 0x81 and 0x86 verify-record forms and the 0x10 version record at offset 8 of the body. 2. Built-in AACS keys + operator plugin slot. Four device keys (covering MKB v01-v82+) and three processing keys (covering v63-v68) compiled directly into the library. Combined with the 31 CSS player keys already in css/auth.rs, DVDs and Blu-rays (AACS 1.0) now decrypt with zero external files. New plugin path at ~/.config/freemkv/local_keys.cfg (same syntax as keydb.cfg) layered additively on top of built-ins and main keydb. `Disc::scan` no longer errors when keydb.cfg is absent; AACS 2.0 / UHD still surfaces a specific error when the disc needs keys none of the layers provide. Public docstrings in CLAUDE.md + README updated to describe the three additive layers (built-ins → keydb.cfg → local_keys.cfg).
140 lines
4.4 KiB
Rust
140 lines
4.4 KiB
Rust
//! Built-in public AACS 1.0 keys.
|
|
//!
|
|
//! Compile-time tables of the device keys (DK) and processing keys (PK)
|
|
//! required for AACS 1.0 MKB processing. These values are well-known
|
|
//! public AACS inputs that cover the MKB version ranges shipped on
|
|
//! retail Blu-ray / UHD discs.
|
|
//!
|
|
//! With these built-ins, libfreemkv can resolve AACS 1.0 encryption for
|
|
//! any disc whose VUK can be derived from MKB + device-key / processing-key
|
|
//! paths — no external keydb.cfg file is required. Operators who want to
|
|
//! supply additional keys (for example, future AACS 2.x derivations) can
|
|
//! drop a `local_keys.cfg` into `$HOME/.config/freemkv/` in the same
|
|
//! format as `keydb.cfg`; see [`crate::aacs::KeyDb::load_or_builtins`].
|
|
|
|
use super::keydb::DeviceKey;
|
|
|
|
/// A built-in device key entry. Mirrors [`DeviceKey`] but stored as a
|
|
/// `const`-friendly POD type with an additional MKB range tag for
|
|
/// diagnostics. Convert via [`BuiltinDeviceKey::to_device_key`].
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub(crate) struct BuiltinDeviceKey {
|
|
pub key: [u8; 16],
|
|
pub device_node: u16,
|
|
pub key_uv: u32,
|
|
pub u_mask_shift: u8,
|
|
/// MKB version range tag (for logging / diagnostics only).
|
|
#[allow(dead_code)]
|
|
pub mkb_range: &'static str,
|
|
}
|
|
|
|
impl BuiltinDeviceKey {
|
|
pub(crate) fn to_device_key(self) -> DeviceKey {
|
|
DeviceKey {
|
|
key: self.key,
|
|
node: self.device_node,
|
|
uv: self.key_uv,
|
|
u_mask_shift: self.u_mask_shift,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Public AACS 1.0 device keys covering MKB versions v01 through v82+.
|
|
///
|
|
/// Each entry contributes a subset-difference path through the MKB tree,
|
|
/// so the four together cover the MKB ranges shipped on retail Blu-ray
|
|
/// and UHD discs to date.
|
|
pub(crate) const BUILTIN_DEVICE_KEYS: &[BuiltinDeviceKey] = &[
|
|
BuiltinDeviceKey {
|
|
key: [
|
|
0x5F, 0xB8, 0x6E, 0xF1, 0x27, 0xC1, 0x9C, 0x17, 0x1E, 0x79, 0x9F, 0x61, 0xC2, 0x7B,
|
|
0xDC, 0x2A,
|
|
],
|
|
device_node: 0x0800,
|
|
key_uv: 0x0000_0400,
|
|
u_mask_shift: 0x17,
|
|
mkb_range: "v01-v48",
|
|
},
|
|
BuiltinDeviceKey {
|
|
key: [
|
|
0x38, 0x84, 0x16, 0x73, 0xE2, 0xB4, 0xE0, 0x51, 0x91, 0x65, 0x98, 0x99, 0x60, 0x6C,
|
|
0xFF, 0xB8,
|
|
],
|
|
device_node: 0x0C00,
|
|
key_uv: 0x0000_0A00,
|
|
u_mask_shift: 0x0B,
|
|
mkb_range: "v49-v71",
|
|
},
|
|
BuiltinDeviceKey {
|
|
key: [
|
|
0x86, 0x1B, 0x37, 0x19, 0xB0, 0x2F, 0x24, 0xBE, 0x6F, 0x1A, 0x30, 0xE2, 0xE3, 0xAB,
|
|
0xEE, 0x94,
|
|
],
|
|
device_node: 0x0C40,
|
|
key_uv: 0x0000_0D00,
|
|
u_mask_shift: 0x0A,
|
|
mkb_range: "v72+",
|
|
},
|
|
BuiltinDeviceKey {
|
|
key: [
|
|
0x7C, 0x06, 0xDE, 0xAE, 0x7F, 0x49, 0xB5, 0x51, 0xDA, 0xF5, 0x38, 0xC8, 0xCF, 0x18,
|
|
0x11, 0xC9,
|
|
],
|
|
device_node: 0x0E20,
|
|
key_uv: 0x0000_0E23,
|
|
u_mask_shift: 0x02,
|
|
mkb_range: "v82+",
|
|
},
|
|
];
|
|
|
|
/// Public AACS 1.0 processing keys for specific MKB versions.
|
|
///
|
|
/// Each value is a precomputed media-key-precursor that resolves MKB
|
|
/// processing for the version range called out beside it. Provided as a
|
|
/// fast path so an exhaustive device-key MKB walk is not required when a
|
|
/// matching PK is available.
|
|
pub(crate) const BUILTIN_PROCESSING_KEYS: &[[u8; 16]] = &[
|
|
// v63
|
|
[
|
|
0x76, 0xDD, 0xD7, 0x09, 0x32, 0x16, 0xD2, 0x8C, 0x15, 0x04, 0x9A, 0x6B, 0x9C, 0x5C, 0x18,
|
|
0xB9,
|
|
],
|
|
// v64-v65
|
|
[
|
|
0x3B, 0x32, 0x3C, 0x7A, 0x9A, 0xFC, 0x09, 0x21, 0x83, 0x1D, 0x24, 0x72, 0x39, 0x82, 0x3D,
|
|
0xE6,
|
|
],
|
|
// v66-v68
|
|
[
|
|
0x7A, 0x4F, 0x40, 0xD8, 0x69, 0x6B, 0x7B, 0x15, 0x9B, 0xE8, 0x17, 0x6C, 0xC9, 0xED, 0xB8,
|
|
0x5C,
|
|
],
|
|
];
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn builtin_device_keys_count() {
|
|
assert_eq!(BUILTIN_DEVICE_KEYS.len(), 4);
|
|
}
|
|
|
|
#[test]
|
|
fn builtin_processing_keys_count() {
|
|
assert_eq!(BUILTIN_PROCESSING_KEYS.len(), 3);
|
|
}
|
|
|
|
#[test]
|
|
fn first_builtin_device_key_value() {
|
|
let expected: [u8; 16] = [
|
|
0x5F, 0xB8, 0x6E, 0xF1, 0x27, 0xC1, 0x9C, 0x17, 0x1E, 0x79, 0x9F, 0x61, 0xC2, 0x7B,
|
|
0xDC, 0x2A,
|
|
];
|
|
assert_eq!(BUILTIN_DEVICE_KEYS[0].key, expected);
|
|
assert_eq!(BUILTIN_DEVICE_KEYS[0].device_node, 0x0800);
|
|
assert_eq!(BUILTIN_DEVICE_KEYS[0].key_uv, 0x0000_0400);
|
|
assert_eq!(BUILTIN_DEVICE_KEYS[0].u_mask_shift, 0x17);
|
|
}
|
|
}
|