mirror of
https://github.com/freemkv/freemkv-unlock.git
synced 2026-10-07 18:13:11 +00:00
fix(ld): bind drives whose reported product_id differs from the generic catalog id
find_by_drive_id gated the product-id-blind four-field fallback behind an empty product_id in 1.7.0, so a drive reporting a specific product_id (e.g. an LG BU40N reporting "BD-RE BU40N") no longer matched its catalog entry (generic "BD-RE"), silently skipping the LibreDrive firmware unlocker — the rip fell through to the AACS host-cert path (LD: no, AACS: yes). Run the four-field fallback (vendor/revision/vendor_specific/firmware_date) unconditionally but ambiguity-guarded: bind a UNIQUE catalog match regardless of the reported product_id (the real-drive case), and require the exact product_id only when catalogued siblings genuinely share the four-field identity — preserving the anti-mis-bind protection the 1.7.0 gate intended. Regression test added. Bump to 1.7.1.
This commit is contained in:
Generated
+1
-1
@@ -142,7 +142,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "freemkv-unlock"
|
name = "freemkv-unlock"
|
||||||
version = "1.7.0"
|
version = "1.7.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes",
|
"aes",
|
||||||
"base64",
|
"base64",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "freemkv-unlock"
|
name = "freemkv-unlock"
|
||||||
version = "1.7.0"
|
version = "1.7.1"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
rust-version = "1.88"
|
rust-version = "1.88"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|||||||
+79
-17
@@ -309,13 +309,19 @@ fn load_from_str(data: &str) -> Result<Profiles> {
|
|||||||
|
|
||||||
/// Find a profile matching a drive's INQUIRY fields.
|
/// Find a profile matching a drive's INQUIRY fields.
|
||||||
///
|
///
|
||||||
/// Per platform section (MT1959-A, then MT1959-B, then Renesas). A drive that
|
/// Per platform section (MT1959-A, then MT1959-B, then Renesas), two passes:
|
||||||
/// reports a `product_id` matches ONLY on the full identity including it — if
|
///
|
||||||
/// no cataloged profile matches exactly, the drive is treated as uncataloged
|
/// 1. Exact match including `product_id` (only when the drive reports one).
|
||||||
/// (no match), never bound to a same-vendor/revision sibling. The looser
|
/// 2. Product-id-blind fallback on the four-field identity
|
||||||
/// four-field pass (vendor/revision/vendor_specific/firmware_date) runs only
|
/// (vendor/revision/vendor_specific/firmware_date). Catalogs store a GENERIC
|
||||||
/// when the drive reports no `product_id` at all. All comparisons are
|
/// `product_id` (e.g. "BD-RE") while drives report a specific one (e.g.
|
||||||
/// whitespace-trimmed. Returns the first section that matches.
|
/// "BD-RE BU40N"), so pass 1 legitimately misses on real drives. Pass 2 binds
|
||||||
|
/// a UNIQUE four-field match regardless of the reported product_id; when two
|
||||||
|
/// cataloged siblings share the four-tuple (differing only by product_id) it
|
||||||
|
/// binds only if the drive reported no product_id to disambiguate with —
|
||||||
|
/// otherwise the drive is uncataloged and must not mis-bind to a sibling.
|
||||||
|
///
|
||||||
|
/// All comparisons are whitespace-trimmed. Returns the first section that matches.
|
||||||
pub fn find_by_drive_id(profiles: &Profiles, drive_id: &crate::DriveId) -> Option<ProfileMatch> {
|
pub fn find_by_drive_id(profiles: &Profiles, drive_id: &crate::DriveId) -> Option<ProfileMatch> {
|
||||||
let v = drive_id.vendor_id.trim();
|
let v = drive_id.vendor_id.trim();
|
||||||
let prod = drive_id.product_id.trim();
|
let prod = drive_id.product_id.trim();
|
||||||
@@ -343,21 +349,46 @@ pub fn find_by_drive_id(profiles: &Profiles, drive_id: &crate::DriveId) -> Optio
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// The product_id-blind pass runs ONLY when the drive reports none: a
|
// Product-id-blind fallback. The catalog stores a GENERIC product_id
|
||||||
// drive that reports a product_id but misses the exact match is an
|
// (e.g. "BD-RE"), while a drive reports a specific one (e.g. "BD-RE
|
||||||
// uncataloged variant, else it'd bind to a sibling's wrong firmware.
|
// BU40N"), so the exact pass above legitimately misses on real drives.
|
||||||
if prod.is_empty()
|
// Fall back to the four-field identity
|
||||||
&& let Some(p) = list.iter().find(|p| {
|
// (vendor/revision/vendor_specific/firmware_date), which is unique per
|
||||||
|
// profile — but guard against MIS-BINDING: when two cataloged siblings
|
||||||
|
// share the four-tuple and differ only by product_id, a drive whose
|
||||||
|
// reported product_id matched neither (above) is an uncataloged variant
|
||||||
|
// and must NOT bind to the wrong sibling's firmware.
|
||||||
|
let four_field: Vec<&DriveProfile> = list
|
||||||
|
.iter()
|
||||||
|
.filter(|p| {
|
||||||
p.identity.vendor_id.trim() == v
|
p.identity.vendor_id.trim() == v
|
||||||
&& p.identity.product_revision.trim() == r
|
&& p.identity.product_revision.trim() == r
|
||||||
&& p.identity.vendor_specific.trim() == vs
|
&& p.identity.vendor_specific.trim() == vs
|
||||||
&& p.identity.firmware_date.trim() == date
|
&& p.identity.firmware_date.trim() == date
|
||||||
})
|
})
|
||||||
{
|
.collect();
|
||||||
return Some(ProfileMatch {
|
match four_field.as_slice() {
|
||||||
profile: p.clone(),
|
// Exactly one four-field match — unambiguous, so bind it regardless
|
||||||
platform,
|
// of the reported product_id. This is the common real-drive case (a
|
||||||
});
|
// generic cataloged product_id vs a specific reported one) and the
|
||||||
|
// pre-1.7.0 behaviour a UHD-capable LG BU40N relied on.
|
||||||
|
[only] => {
|
||||||
|
return Some(ProfileMatch {
|
||||||
|
profile: (*only).clone(),
|
||||||
|
platform,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Two-plus cataloged siblings share the four-tuple. Only bind when
|
||||||
|
// the drive reported NO product_id to disambiguate with; a non-empty
|
||||||
|
// product_id that missed the exact pass is an uncataloged variant and
|
||||||
|
// must not mis-bind to a sibling's firmware.
|
||||||
|
[first, ..] if prod.is_empty() => {
|
||||||
|
return Some(ProfileMatch {
|
||||||
|
profile: (*first).clone(),
|
||||||
|
platform,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -427,6 +458,37 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// REGRESSION (1.7.0): a real drive reports a SPECIFIC product_id (e.g.
|
||||||
|
/// "BD-RE BU40N") while the catalog stores a GENERIC one ("BD-RE"), so the
|
||||||
|
/// exact pass misses. When the four-field identity is UNIQUE it must still
|
||||||
|
/// bind — the pre-1.7.0 behaviour a UHD LG BU40N relies on for LibreDrive
|
||||||
|
/// unlock. 1.7.0 gated the four-field pass behind an empty product_id, so it
|
||||||
|
/// silently stopped matching and the firmware unlocker was skipped entirely.
|
||||||
|
#[test]
|
||||||
|
fn find_by_drive_id_specific_product_id_binds_unique_four_field() {
|
||||||
|
use serde_json::json;
|
||||||
|
let profiles: Profiles = serde_json::from_str(
|
||||||
|
&json!({
|
||||||
|
"mt1959_a": [
|
||||||
|
{"identity": {"vendor_id":"HL-DT-ST","product_id":"BD-RE",
|
||||||
|
"product_revision":"1.03","vendor_specific":"NM00000",
|
||||||
|
"firmware_date":"211810241934"},
|
||||||
|
"signature":"12345678","firmware":""}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.to_string(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Drive reports the specific marketing product_id; catalog has "BD-RE".
|
||||||
|
let mut id = make_drive_id("HL-DT-ST", "1.03", "NM00000", "211810241934");
|
||||||
|
id.product_id = "BD-RE BU40N".to_string();
|
||||||
|
let m = find_by_drive_id(&profiles, &id)
|
||||||
|
.expect("unique four-field identity must match despite the product_id mismatch");
|
||||||
|
assert_eq!(m.profile.signature, [0x12, 0x34, 0x56, 0x78]);
|
||||||
|
assert_eq!(m.platform, Platform::Mt1959A);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_find_known_drive() {
|
fn test_find_known_drive() {
|
||||||
let profiles = load_bundled().unwrap();
|
let profiles = load_bundled().unwrap();
|
||||||
|
|||||||
Reference in New Issue
Block a user