Files
libfreemkv/src/aacs/builtin_keys.rs
T
MattJackson e635c9556f v0.25.9: built-in AACS keys + plugin slot + MKB record-type fix
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 project docs + README updated to describe the
three additive layers (built-ins → keydb.cfg → local_keys.cfg).
2026-05-20 09:00:32 -07:00

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);
}
}