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).
This commit is contained in:
+10
-4
@@ -157,14 +157,20 @@ impl Disc {
|
||||
pub(super) fn resolve_encryption(
|
||||
udf_fs: &udf::UdfFs,
|
||||
reader: &mut dyn SectorSource,
|
||||
keydb_path: &std::path::Path,
|
||||
keydb_path: Option<&std::path::Path>,
|
||||
handshake: Option<&HandshakeResult>,
|
||||
) -> Result<AacsState> {
|
||||
use crate::aacs::{self, KeyDb};
|
||||
|
||||
let keydb = KeyDb::load(keydb_path).map_err(|_| Error::KeydbLoad {
|
||||
path: keydb_path.display().to_string(),
|
||||
})?;
|
||||
// Built-in AACS 1.0 keys are always available. When a keydb.cfg
|
||||
// path is supplied, layer it on top; otherwise fall back to
|
||||
// built-ins (plus the operator local-plugin slot, if any).
|
||||
let keydb = match keydb_path {
|
||||
Some(path) => KeyDb::load(path).map_err(|_| Error::KeydbLoad {
|
||||
path: path.display().to_string(),
|
||||
})?,
|
||||
None => KeyDb::load_or_builtins(),
|
||||
};
|
||||
|
||||
// Read AACS files from disc/image via UDF
|
||||
let uk_ro_data = udf_fs
|
||||
|
||||
+26
-31
@@ -1116,40 +1116,35 @@ impl Disc {
|
||||
udf_fs.find_dir("/AACS").is_some() || udf_fs.find_dir("/BDMV/AACS").is_some();
|
||||
|
||||
let (aacs, aacs_error) = if encrypted {
|
||||
match opts.resolve_keydb() {
|
||||
Some(keydb_path) => {
|
||||
match Self::resolve_encryption(&udf_fs, reader, &keydb_path, handshake.as_ref())
|
||||
{
|
||||
Ok(state) => (Some(state), None),
|
||||
Err(e) => {
|
||||
tracing::warn!(
|
||||
target: "freemkv::disc",
|
||||
phase = "scan_aacs_resolve_failed",
|
||||
error_code = e.code(),
|
||||
keydb = %keydb_path.display(),
|
||||
handshake_ok = handshake.is_some(),
|
||||
"AACS key resolution failed"
|
||||
);
|
||||
(None, Some(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// KEYDB is now optional: the library ships built-in AACS 1.0
|
||||
// device + processing keys, so a missing keydb.cfg just falls
|
||||
// back to the built-ins (plus the operator local-plugin slot,
|
||||
// if any). External keydb.cfg layers on top when supplied.
|
||||
let keydb_path = opts.resolve_keydb();
|
||||
if keydb_path.is_none() {
|
||||
tracing::debug!(
|
||||
target: "freemkv::disc",
|
||||
phase = "scan_aacs_builtins_only",
|
||||
"no external KEYDB found; resolving with built-in AACS 1.0 keys"
|
||||
);
|
||||
}
|
||||
match Self::resolve_encryption(
|
||||
&udf_fs,
|
||||
reader,
|
||||
keydb_path.as_deref(),
|
||||
handshake.as_ref(),
|
||||
) {
|
||||
Ok(state) => (Some(state), None),
|
||||
Err(e) => {
|
||||
tracing::warn!(
|
||||
target: "freemkv::disc",
|
||||
phase = "scan_aacs_no_keydb",
|
||||
"encrypted disc but no KEYDB found in search paths"
|
||||
phase = "scan_aacs_resolve_failed",
|
||||
error_code = e.code(),
|
||||
keydb = ?keydb_path.as_ref().map(|p| p.display().to_string()),
|
||||
handshake_ok = handshake.is_some(),
|
||||
"AACS key resolution failed"
|
||||
);
|
||||
// Reuse KeydbLoad with sentinel path — adding a new Error
|
||||
// variant would be a breaking change for downstream
|
||||
// exhaustive matches. The path string makes the cause
|
||||
// unambiguous to autorip's message switch.
|
||||
(
|
||||
None,
|
||||
Some(crate::error::Error::KeydbLoad {
|
||||
path: String::from("<no keydb in search paths>"),
|
||||
}),
|
||||
)
|
||||
(None, Some(e))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user