diff --git a/Cargo.toml b/Cargo.toml index 4192457..9cd5204 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "freemkv-unlock" -version = "1.2.2" +version = "1.2.3" edition = "2024" rust-version = "1.86" license = "AGPL-3.0-only" diff --git a/src/aacs/mod.rs b/src/aacs/mod.rs index 0c6d0ce..f830ac6 100644 --- a/src/aacs/mod.rs +++ b/src/aacs/mod.rs @@ -48,15 +48,17 @@ impl Unlocker for AacsCert { "AACS" } - fn matches(&self, ctx: &UnlockCtx) -> bool { - ctx.kind == DiscKind::Aacs - } - - fn unlock( + /// AACS removes BUS encryption via the host-cert handshake; it provides no + /// drive features. Self-guards on the disc kind (the consumer iterates every + /// unlocker's `unlock_bus`, so a non-AACS disc must decline here). + fn unlock_bus( &self, scsi: &mut dyn ScsiTransport, ctx: &UnlockCtx, ) -> std::result::Result { + if ctx.kind != DiscKind::Aacs { + return Err(UnlockError::NotApplicable); + } if ctx.host_certs.is_empty() { // No host cert to authenticate with — the consumer falls back to a // VID-less / keysource path. @@ -81,18 +83,29 @@ mod tests { crate::DriveId::default() } - /// AacsCert matches only `DiscKind::Aacs`. + /// `unlock_bus` self-guards on the disc kind: on a non-AACS disc it declines + /// (`NotApplicable`) WITHOUT touching the transport, so iterating it on a + /// CSS/unknown disc is safe. #[test] - fn matches_only_aacs_kind() { + fn unlock_bus_declines_non_aacs_kinds() { + struct DeadTransport; + impl ScsiTransport for DeadTransport { + fn execute( + &mut self, + _cdb: &[u8], + _dir: crate::scsi::DataDirection, + _data: &mut [u8], + _timeout_ms: u32, + ) -> crate::scsi::Result { + panic!("transport must not be touched on a non-AACS disc"); + } + } let id = id(); - let u = AacsCert::new(); + let mut t = DeadTransport; for k in [DiscKind::Unknown, DiscKind::Unencrypted, DiscKind::Css] { - assert!( - !u.matches(&UnlockCtx::new(&id, k, &[])), - "must not match {k:?}" - ); + let r = AacsCert::new().unlock_bus(&mut t, &UnlockCtx::new(&id, k, &[])); + assert_eq!(r.unwrap_err(), UnlockError::NotApplicable, "declines {k:?}"); } - assert!(u.matches(&UnlockCtx::new(&id, DiscKind::Aacs, &[]))); } /// With no host certs there is nothing to authenticate with → NoUsableHostCert, @@ -113,7 +126,7 @@ mod tests { } let id = id(); let mut t = DeadTransport; - let r = AacsCert::new().unlock(&mut t, &UnlockCtx::new(&id, DiscKind::Aacs, &[])); + let r = AacsCert::new().unlock_bus(&mut t, &UnlockCtx::new(&id, DiscKind::Aacs, &[])); assert_eq!(r.unwrap_err(), UnlockError::NoUsableHostCert); } } diff --git a/src/css/mod.rs b/src/css/mod.rs index 04af624..542a927 100644 --- a/src/css/mod.rs +++ b/src/css/mod.rs @@ -164,11 +164,10 @@ impl crate::Unlocker for CssUnlocker { "CSS" } - fn matches(&self, ctx: &crate::UnlockCtx) -> bool { - ctx.kind == crate::DiscKind::Css - } - - fn unlock( + /// CSS removes the scrambled-sector barrier (a bus-level concern); it + /// provides no drive features. Self-guards against the hardware (below), so + /// it declines cleanly when the consumer iterates it on a non-DVD. + fn unlock_bus( &self, scsi: &mut dyn ScsiTransport, _ctx: &crate::UnlockCtx, @@ -923,27 +922,28 @@ mod tests { assert_eq!(cdb[9], 0x04, "low byte of 2052-byte transfer"); } - /// The CssUnlocker matches ONLY `DiscKind::Css` (never fires during - /// drive-prep or on a Blu-ray). + /// CssUnlocker provides bus removal only — it never provides drive features. #[test] - fn css_unlocker_matches_only_css_kind() { - use crate::{DiscKind, DriveId, UnlockCtx, Unlocker}; - let id = DriveId { - vendor_id: "FAKEVNDR".to_string(), - ..Default::default() - }; - - let u = CssUnlocker::new(); - assert!( - u.matches(&UnlockCtx::new(&id, DiscKind::Css, &[])), - "matches a CSS DVD" - ); - for k in [DiscKind::Unknown, DiscKind::Unencrypted, DiscKind::Aacs] { - assert!( - !u.matches(&UnlockCtx::new(&id, k, &[])), - "CssUnlocker must not match {k:?}" - ); + fn css_unlocker_provides_no_features() { + use crate::scsi::{DataDirection, ScsiResult}; + use crate::{DiscKind, DriveId, UnlockCtx, UnlockError, Unlocker}; + struct DeadTransport; + impl ScsiTransport for DeadTransport { + fn execute( + &mut self, + _cdb: &[u8], + _dir: DataDirection, + _data: &mut [u8], + _timeout_ms: u32, + ) -> crate::scsi::Result { + panic!("unlock_features must not touch the transport"); + } } + let id = DriveId::default(); + let mut t = DeadTransport; + let r = + CssUnlocker::new().unlock_features(&mut t, &UnlockCtx::new(&id, DiscKind::Css, &[])); + assert_eq!(r.unwrap_err(), UnlockError::NotApplicable); } /// Defense in depth: even when the caller declares `DiscKind::Css`, the @@ -994,7 +994,7 @@ mod tests { }; let mut t = BdTransport { non_config_cdbs: 0 }; - let r = CssUnlocker::new().unlock(&mut t, &UnlockCtx::new(&id, DiscKind::Css, &[])); + let r = CssUnlocker::new().unlock_bus(&mut t, &UnlockCtx::new(&id, DiscKind::Css, &[])); assert_eq!( r.unwrap_err(), UnlockError::NotApplicable, diff --git a/src/ld/mod.rs b/src/ld/mod.rs index 7ca6d8a..48290fc 100644 --- a/src/ld/mod.rs +++ b/src/ld/mod.rs @@ -116,26 +116,17 @@ impl LibreDrive { } } -impl Unlocker for LibreDrive { - /// Firmware unlock is a DRIVE-PREP concern: it runs before the disc kind is - /// probed (`kind == Unknown`) and keys off the drive identity. It must NOT - /// fire during the later content-keyed dispatch (Aacs/Css), or a DVD/Blu-ray - /// in a profiled drive would be re-firmware-unlocked. - fn name(&self) -> &'static str { - "LibreDrive" - } - - fn matches(&self, ctx: &UnlockCtx) -> bool { - ctx.kind == crate::DiscKind::Unknown && profile::find_bundled(ctx.drive_id).is_some() - } - - /// Firmware-unlock the drive and report its OEM Volume ID. The unlocked drive - /// serves CLEAR content, so `drive_unlocked: true` and there is no bus key. +impl LibreDrive { + /// The MediaTek firmware unlock. Because LibreDrive removes AACS bus + /// encryption AT THE DRIVE (the unlocked drive serves CLEAR content), this ONE + /// operation satisfies BOTH the drive-features and the bus-removal capability + /// — so `unlock_features` and `unlock_bus` both delegate here. The result + /// carries `drive_unlocked: true` (no bus key needed) and the OEM Volume ID. /// - /// A no-firmware-route drive (Renesas) returns `NotApplicable` (fall through); - /// a transport fault propagates as `Transport`; a firmware failure that isn't - /// a dead bus also falls through (`NotApplicable`). - fn unlock( + /// A no-firmware-route drive (Renesas / no profile) returns `NotApplicable`; a + /// transport fault propagates as `Transport`; any other firmware failure also + /// falls through as `NotApplicable`. + fn firmware_unlock( &self, scsi: &mut dyn ScsiTransport, ctx: &UnlockCtx, @@ -145,19 +136,17 @@ impl Unlocker for LibreDrive { return Err(UnlockError::NotApplicable); }; if matches!(m.platform, profile::Platform::Renesas) { - // Renesas firmware unlock is not implemented — fall through to cert. + // Renesas is a different platform (handled by the Renesas unlocker). return Err(UnlockError::NotApplicable); } let is_variant_b = matches!(m.platform, profile::Platform::Mt1959B); use platform::PlatformDriver; let mut mt = platform::mt1959::Mt1959::new(m.profile, is_variant_b); - // Firmware unlock. A transport fault → UnlockError::Transport; any other - // firmware failure → NotApplicable (via From). + // A transport fault → UnlockError::Transport; any other firmware failure + // → NotApplicable (via From). mt.init(scsi)?; // Prime the per-region speed table (best-effort — must not fail the unlock). let _ = mt.probe_disc(scsi); - // The unlocked drive hands back its Volume ID; firmware serves clear - // content, so no bus key and drive_unlocked = true. let vid = self.read_oem_vid(scsi, id)?; Ok(Unlocked { vid, @@ -167,6 +156,33 @@ impl Unlocker for LibreDrive { } } +impl Unlocker for LibreDrive { + fn name(&self) -> &'static str { + "LibreDrive" + } + + /// LibreDrive provides drive features (riplock/speed, OEM VID) — and, because + /// its firmware unlock serves clear content, bus removal comes free with it. + fn unlock_features( + &self, + scsi: &mut dyn ScsiTransport, + ctx: &UnlockCtx, + ) -> std::result::Result { + self.firmware_unlock(scsi, ctx) + } + + /// Same firmware code as [`unlock_features`]: LibreDrive removes the bus at + /// the drive. In practice the consumer skips this because drive-prep already + /// set `drive_unlocked`; it's here for completeness / a bus-first call order. + fn unlock_bus( + &self, + scsi: &mut dyn ScsiTransport, + ctx: &UnlockCtx, + ) -> std::result::Result { + self.firmware_unlock(scsi, ctx) + } +} + #[cfg(test)] mod tests { use super::*; @@ -212,6 +228,7 @@ mod tests { fn make_drive_id(vendor: &str, rev: &str, vs: &str, date: &str) -> DriveId { DriveId { vendor_id: vendor.to_string(), + product_id: String::new(), product_revision: rev.to_string(), vendor_specific: vs.to_string(), firmware_date: date.to_string(), @@ -283,8 +300,8 @@ mod tests { assert_eq!(got, None); } - /// `unlock` on a drive with no matching profile → `NotApplicable` (fall - /// through), short-circuiting before any firmware handshake. + /// `unlock_features` on a drive with no matching profile → `NotApplicable` + /// (fall through), short-circuiting before any firmware handshake. #[test] fn unlock_no_profile_is_not_applicable() { let mut t = FakeTransport { @@ -292,7 +309,7 @@ mod tests { bytes_transferred: 36, }; let err = LibreDrive::new() - .unlock( + .unlock_features( &mut t, &ctx(&make_drive_id("FAKE-VND", "9.99", "XX12345", "")), ) diff --git a/src/ld/platform/mt1959/mod.rs b/src/ld/platform/mt1959/mod.rs index 144f9d4..b5c6957 100644 --- a/src/ld/platform/mt1959/mod.rs +++ b/src/ld/platform/mt1959/mod.rs @@ -487,6 +487,7 @@ mod tests { DriveProfile { identity: Identity { vendor_id: "TEST".into(), + product_id: String::new(), product_revision: String::new(), vendor_specific: String::new(), firmware_date: String::new(), diff --git a/src/ld/profile.rs b/src/ld/profile.rs index d1df7e8..0688b12 100644 --- a/src/ld/profile.rs +++ b/src/ld/profile.rs @@ -31,6 +31,8 @@ pub struct Identity { #[serde(default)] pub vendor_id: String, #[serde(default)] + pub product_id: String, + #[serde(default)] pub product_revision: String, #[serde(default)] pub vendor_specific: String, @@ -283,15 +285,16 @@ fn load_from_str(data: &str) -> Result { /// Find a profile matching a drive's INQUIRY fields. /// -/// Two-pass per platform section (MT1959-A, then MT1959-B, then Renesas): -/// first an exact match including `firmware_date`, then — if none — a -/// looser match on vendor / revision / vendor-specific only. The exact -/// pass wins so a drive with a known firmware date binds to its precise -/// profile; the looser pass lets a drive whose firmware date we don't have -/// on file still match a same-model profile. All comparisons are -/// whitespace-trimmed. Returns the first section that yields a match. +/// Per platform section (MT1959-A, then MT1959-B, then Renesas), tried in +/// order of decreasing specificity: full match including `product_id` and +/// `firmware_date` (skipped when the drive reports no product), then the same +/// without `product_id`. Both require `firmware_date`; every profile carries a +/// unique identity under these fields, so a match is exact — no looser +/// vendor/revision fallback that could pick the wrong same-model variant. All +/// comparisons are whitespace-trimmed. Returns the first section that matches. pub fn find_by_drive_id(profiles: &Profiles, drive_id: &crate::DriveId) -> Option { let v = drive_id.vendor_id.trim(); + let prod = drive_id.product_id.trim(); let r = drive_id.product_revision.trim(); let vs = drive_id.vendor_specific.trim(); let date = drive_id.firmware_date.trim(); @@ -301,22 +304,26 @@ pub fn find_by_drive_id(profiles: &Profiles, drive_id: &crate::DriveId) -> Optio (Platform::Mt1959B, &profiles.mt1959_b), (Platform::Renesas, &profiles.renesas), ] { - if let Some(p) = list.iter().find(|p| { - p.identity.vendor_id.trim() == v - && p.identity.product_revision.trim() == r - && p.identity.vendor_specific.trim() == vs - && p.identity.firmware_date.trim() == date - }) { - return Some(ProfileMatch { - profile: p.clone(), - platform, - }); + if !prod.is_empty() { + if let Some(p) = list.iter().find(|p| { + p.identity.vendor_id.trim() == v + && p.identity.product_id.trim() == prod + && p.identity.product_revision.trim() == r + && p.identity.vendor_specific.trim() == vs + && p.identity.firmware_date.trim() == date + }) { + return Some(ProfileMatch { + profile: p.clone(), + platform, + }); + } } if let Some(p) = list.iter().find(|p| { p.identity.vendor_id.trim() == v && p.identity.product_revision.trim() == r && p.identity.vendor_specific.trim() == vs + && p.identity.firmware_date.trim() == date }) { return Some(ProfileMatch { profile: p.clone(), @@ -336,12 +343,51 @@ mod tests { fn make_drive_id(vendor: &str, rev: &str, vs: &str, date: &str) -> DriveId { DriveId { vendor_id: vendor.to_string(), + product_id: String::new(), product_revision: rev.to_string(), vendor_specific: vs.to_string(), firmware_date: date.to_string(), } } + /// When two profiles share vendor/rev/vs/date and differ only in product_id, + /// the full pass binds to the one whose product matches; the looser passes + /// would return the first regardless. + #[test] + fn find_by_drive_id_product_id_breaks_a_tie() { + use serde_json::json; + let profiles: Profiles = serde_json::from_str( + &json!({ + "mt1959_a": [ + {"identity": {"vendor_id":"TIE","product_id":"MODEL-A", + "product_revision":"1.00","vendor_specific":"XX00000", + "firmware_date":"200001010000"}, + "signature":"aaaaaaaa","firmware":""}, + {"identity": {"vendor_id":"TIE","product_id":"MODEL-B", + "product_revision":"1.00","vendor_specific":"XX00000", + "firmware_date":"200001010000"}, + "signature":"bbbbbbbb","firmware":""} + ] + }) + .to_string(), + ) + .unwrap(); + + let mut id = make_drive_id("TIE", "1.00", "XX00000", "200001010000"); + id.product_id = "MODEL-B".to_string(); + let m = find_by_drive_id(&profiles, &id).unwrap(); + assert_eq!( + m.profile.signature, + [0xbb, 0xbb, 0xbb, 0xbb], + "product_id must select MODEL-B over the first entry" + ); + + // No product_id → falls back to the 4-field pass → first entry. + let id0 = make_drive_id("TIE", "1.00", "XX00000", "200001010000"); + let m0 = find_by_drive_id(&profiles, &id0).unwrap(); + assert_eq!(m0.profile.signature, [0xaa, 0xaa, 0xaa, 0xaa]); + } + #[test] fn test_find_known_drive() { let profiles = load_bundled().unwrap(); @@ -512,14 +558,12 @@ mod tests { ); } - /// find_by_drive_id: loose match (no date) still works when an entry has - /// the same vendor/revision/vs but an unknown firmware_date. - /// Mutation: making the loose pass require a date match means "no date" drives - /// always return None even though a same-model profile exists. + /// find_by_drive_id: a drive whose firmware_date does NOT match any profile + /// gets no match — there is no loose vendor/rev/vs fallback that could bind + /// the wrong same-model variant. #[test] - fn find_by_drive_id_loose_match_when_date_unknown() { + fn find_by_drive_id_no_match_when_date_differs() { use serde_json::json; - // "LOOSEDR " fills 8 bytes; trim() → "LOOSEDR". let profiles_json = json!({ "mt1959_a": [ { @@ -537,15 +581,9 @@ mod tests { .to_string(); let profiles: Profiles = serde_json::from_str(&profiles_json).unwrap(); - // Drive with an unknown firmware date — no exact match, loose match should work. - // "LOOSEDR " fills 8 bytes; "000000000000" is the unknown date. + // Same vendor/rev/vs but a different date — must NOT match. let id = make_drive_id("LOOSEDR ", "2.00", "YY11111", "000000000000"); - let m = find_by_drive_id(&profiles, &id).unwrap(); - assert_eq!( - m.profile.signature, - [0xde, 0xad, 0xbe, 0xef], - "loose match must bind the same-model profile when date differs" - ); + assert!(find_by_drive_id(&profiles, &id).is_none()); } /// load_from_str (via load_bundled) returns ProfileParse on invalid JSON. diff --git a/src/ld/profiles.json b/src/ld/profiles.json index bd873b1..fa5c1d1 100644 --- a/src/ld/profiles.json +++ b/src/ld/profiles.json @@ -3,6 +3,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.01", "vendor_specific": "NM00100", "firmware_date": "211711202000" @@ -26,6 +27,7 @@ { "identity": { "vendor_id": "ASUS", + "product_id": "BW-16D1X-U", "product_revision": "A104", "vendor_specific": "WM01001", "firmware_date": "211901041047" @@ -49,6 +51,7 @@ { "identity": { "vendor_id": "ASUS", + "product_id": "BW-16D1H-U", "product_revision": "A203", "vendor_specific": "WM01501", "firmware_date": "211801111117" @@ -72,6 +75,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.02", "vendor_specific": "NM01201", "firmware_date": "211711301218" @@ -95,6 +99,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.04", "vendor_specific": "NM01201", "firmware_date": "211901041351" @@ -118,6 +123,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.04", "vendor_specific": "NM01701", "firmware_date": "211901041338" @@ -141,6 +147,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.01", "vendor_specific": "NM00200", "firmware_date": "211711231626" @@ -164,6 +171,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.04", "vendor_specific": "NM01701", "firmware_date": "211901041342" @@ -187,6 +195,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.02", "vendor_specific": "NM00500", "firmware_date": "211711201953" @@ -210,6 +219,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "NM00100", "firmware_date": "211711211006" @@ -233,6 +243,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "U101", "vendor_specific": "MM01201", "firmware_date": "211711301153" @@ -256,6 +267,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.01", "vendor_specific": "NM00500", "firmware_date": "211711161545" @@ -279,6 +291,7 @@ { "identity": { "vendor_id": "ASUS", + "product_id": "BW-16D1H-U", "product_revision": "A204", "vendor_specific": "WM01001", "firmware_date": "211901041044" @@ -302,6 +315,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.01", "vendor_specific": "NM00000", "firmware_date": "211711281847" @@ -325,6 +339,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "BN12", "vendor_specific": "0M01001", "firmware_date": "211905141415" @@ -348,6 +363,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "NM00500", "firmware_date": "211804030945" @@ -371,6 +387,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.03", "vendor_specific": "NM00400", "firmware_date": "212004211008" @@ -394,6 +411,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.01", "vendor_specific": "RM01801", "firmware_date": "211910161032" @@ -417,6 +435,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "U100", "vendor_specific": "3M01801", "firmware_date": "211910161036" @@ -440,6 +459,7 @@ { "identity": { "vendor_id": "ASUS", + "product_id": "BW-16D1HT", "product_revision": "3.03", "vendor_specific": "WM00000", "firmware_date": "211801191558" @@ -463,6 +483,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.03", "vendor_specific": "NM00800", "firmware_date": "212005070917" @@ -486,6 +507,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BDDVDRW", "product_revision": "1.01", "vendor_specific": "NM00200", "firmware_date": "211711231550" @@ -509,6 +531,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.02", "vendor_specific": "NM00800", "firmware_date": "212005070935" @@ -532,6 +555,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.03", "vendor_specific": "NM00300", "firmware_date": "212107081603" @@ -555,6 +579,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.03", "vendor_specific": "NM00400", "firmware_date": "212004210958" @@ -578,6 +603,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.02", "vendor_specific": "NM01101", "firmware_date": "211901041359" @@ -601,6 +627,7 @@ { "identity": { "vendor_id": "ASUS", + "product_id": "BW-16D1HT", "product_revision": "3.10", "vendor_specific": "WM01601", "firmware_date": "211901041014" @@ -624,6 +651,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.04", "vendor_specific": "NM00500", "firmware_date": "212005061142" @@ -647,6 +675,7 @@ { "identity": { "vendor_id": "ASUS", + "product_id": "BC-12D2HT", "product_revision": "3.01", "vendor_specific": "WM00900", "firmware_date": "211711151926" @@ -670,6 +699,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.02", "vendor_specific": "NM01801", "firmware_date": "211905031515" @@ -693,6 +723,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.01", "vendor_specific": "NM01801", "firmware_date": "211905031534" @@ -716,6 +747,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.03", "vendor_specific": "NM00600", "firmware_date": "212005081010" @@ -739,6 +771,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BDDVDRW", "product_revision": "1.01", "vendor_specific": "NM00400", "firmware_date": "211711211606" @@ -762,6 +795,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.05", "vendor_specific": "NM00400", "firmware_date": "212004211049" @@ -785,6 +819,7 @@ { "identity": { "vendor_id": "ASUS", + "product_id": "BC-12B1ST", "product_revision": "3.01", "vendor_specific": "WM00900", "firmware_date": "211711291725" @@ -808,6 +843,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BDDVDRW", "product_revision": "1.03", "vendor_specific": "NM00800", "firmware_date": "212005080959" @@ -831,6 +867,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.03", "vendor_specific": "NM00000", "firmware_date": "211711201704" @@ -854,6 +891,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.02", "vendor_specific": "NM00300", "firmware_date": "212107081556" @@ -877,6 +915,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.03", "vendor_specific": "NM00800", "firmware_date": "212005070924" @@ -900,6 +939,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.01", "vendor_specific": "RM00000", "firmware_date": "211712221221" @@ -923,6 +963,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.V5", "vendor_specific": "NM00900", "firmware_date": "211802070957" @@ -946,6 +987,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "BU12", "vendor_specific": "OM01001", "firmware_date": "211902230922" @@ -969,6 +1011,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "NM00200", "firmware_date": "211711211720" @@ -992,6 +1035,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.03", "vendor_specific": "NM00800", "firmware_date": "212005070929" @@ -1015,6 +1059,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.02", "vendor_specific": "NM00600", "firmware_date": "212005081014" @@ -1038,6 +1083,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.01", "vendor_specific": "NM00200", "firmware_date": "211711231646" @@ -1061,6 +1107,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.04", "vendor_specific": "NM00300", "firmware_date": "212005081025" @@ -1084,6 +1131,7 @@ { "identity": { "vendor_id": "ASUS", + "product_id": "BC-12B1ST", "product_revision": "3.11", "vendor_specific": "WM00300", "firmware_date": "211902271321" @@ -1107,6 +1155,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.02", "vendor_specific": "NM00700", "firmware_date": "211811011016" @@ -1130,6 +1179,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.03", "vendor_specific": "NM00600", "firmware_date": "211711211658" @@ -1153,6 +1203,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.01", "vendor_specific": "NM00000", "firmware_date": "211712071200" @@ -1176,6 +1227,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.02", "vendor_specific": "RM00200", "firmware_date": "212012011716" @@ -1199,6 +1251,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.05", "vendor_specific": "NM00900", "firmware_date": "212005061444" @@ -1222,6 +1275,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "FR07", "vendor_specific": "JM02202", "firmware_date": "211801221536" @@ -1245,6 +1299,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.01", "vendor_specific": "SM00800", "firmware_date": "212009071143" @@ -1268,6 +1323,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.03", "vendor_specific": "NM00000", "firmware_date": "211810241934" @@ -1291,6 +1347,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.01", "vendor_specific": "NM00200", "firmware_date": "211711231615" @@ -1314,6 +1371,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.02", "vendor_specific": "NM00100", "firmware_date": "211810291936" @@ -1337,6 +1395,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.01", "vendor_specific": "NM00500", "firmware_date": "211711161557" @@ -1360,6 +1419,7 @@ { "identity": { "vendor_id": "ASUS", + "product_id": "BC-12D2HT", "product_revision": "3.11", "vendor_specific": "WM00300", "firmware_date": "211902271319" @@ -1383,6 +1443,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.05", "vendor_specific": "NM00900", "firmware_date": "212005061440" @@ -1406,6 +1467,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.05", "vendor_specific": "NM00600", "firmware_date": "212005061331" @@ -1429,6 +1491,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BDDVDRW", "product_revision": "1.03", "vendor_specific": "NM00800", "firmware_date": "212005080957" @@ -1452,6 +1515,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.03", "vendor_specific": "NM00000", "firmware_date": "211711201943" @@ -1475,6 +1539,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.03", "vendor_specific": "NM00600", "firmware_date": "211711211653" @@ -1498,6 +1563,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "BN11", "vendor_specific": "0M00300", "firmware_date": "211711201351" @@ -1523,6 +1589,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "0420", "vendor_specific": "N000000", "firmware_date": "211605241901" @@ -1543,6 +1610,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.03", "vendor_specific": "N0A02A0", "firmware_date": "211312061510" @@ -1563,6 +1631,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.01", "vendor_specific": "N1A12A1", "firmware_date": "211304042250" @@ -1583,6 +1652,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BDDVDRW", "product_revision": "AS00", "vendor_specific": "S002102", "firmware_date": "211306141459" @@ -1603,6 +1673,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.01", "vendor_specific": "N000500", "firmware_date": "211511051004" @@ -1623,6 +1694,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N1A37A3", "firmware_date": "211204261630" @@ -1643,6 +1715,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N004304", "firmware_date": "211412161016" @@ -1663,6 +1736,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.D3", "vendor_specific": "N000700", "firmware_date": "211602041103" @@ -1683,6 +1757,7 @@ { "identity": { "vendor_id": "ASUS", + "product_id": "BW-12B1ST", "product_revision": "1.00", "vendor_specific": "E101701", "firmware_date": "211304231147" @@ -1703,6 +1778,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BDDVDRW", "product_revision": "1.00", "vendor_specific": "N003203", "firmware_date": "211412161022" @@ -1723,6 +1799,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N001501", "firmware_date": "211504221534" @@ -1743,6 +1820,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BDDVDRW", "product_revision": "1.00", "vendor_specific": "N0A20A2", "firmware_date": "211306131532" @@ -1763,6 +1841,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.02", "vendor_specific": "N0C00C0", "firmware_date": "211505261638" @@ -1783,6 +1862,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N002502", "firmware_date": "211508191146" @@ -1803,6 +1883,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "AS00", "vendor_specific": "U101901", "firmware_date": "211304242023" @@ -1823,6 +1904,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N1A29A2", "firmware_date": "211210311913" @@ -1843,6 +1925,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "G005705", "firmware_date": "211504221429" @@ -1863,6 +1946,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.02", "vendor_specific": "N0A02A0", "firmware_date": "211312061535" @@ -1883,6 +1967,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "R004504", "firmware_date": "211703231903" @@ -1903,6 +1988,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N1A29A2", "firmware_date": "211210311917" @@ -1923,6 +2009,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.03", "vendor_specific": "N0A03A0", "firmware_date": "211403261115" @@ -1943,6 +2030,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "BU10", "vendor_specific": "O002602", "firmware_date": "211701060941" @@ -1963,6 +2051,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.01", "vendor_specific": "N000500", "firmware_date": "211511050956" @@ -1983,6 +2072,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.01", "vendor_specific": "N001101", "firmware_date": "211703130907" @@ -2003,6 +2093,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.01", "vendor_specific": "N1A12A1", "firmware_date": "211304042319" @@ -2023,6 +2114,7 @@ { "identity": { "vendor_id": "ASUS", + "product_id": "BW-16D1HT", "product_revision": "1.01", "vendor_specific": "W100600", "firmware_date": "211302051930" @@ -2043,6 +2135,7 @@ { "identity": { "vendor_id": "ASUS", + "product_id": "BW-14D1XT", "product_revision": "1.00", "vendor_specific": "M103703", "firmware_date": "210000000000" @@ -2063,6 +2156,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N002802", "firmware_date": "211702151823" @@ -2083,6 +2177,7 @@ { "identity": { "vendor_id": "hp", + "product_id": "BDRW", "product_revision": "BW20", "vendor_specific": "P018518", "firmware_date": "211504221413" @@ -2103,6 +2198,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "BU10", "vendor_specific": "O003703", "firmware_date": "211705301728" @@ -2123,6 +2219,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "A102", "vendor_specific": "D0A22A2", "firmware_date": "211303262335" @@ -2143,6 +2240,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.01", "vendor_specific": "N1A12A1", "firmware_date": "211304042252" @@ -2163,6 +2261,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BDDVDRW", "product_revision": "1.02", "vendor_specific": "N0A05A0", "firmware_date": "211404241908" @@ -2183,6 +2282,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N000900", "firmware_date": "211609091048" @@ -2203,6 +2303,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N0G43G4", "firmware_date": "211206262029" @@ -2223,6 +2324,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N005505", "firmware_date": "211504221517" @@ -2243,6 +2345,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "CCT5", "vendor_specific": "V000700", "firmware_date": "211705182023" @@ -2263,6 +2366,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.02", "vendor_specific": "N001901", "firmware_date": "211703101630" @@ -2283,6 +2387,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BDDVDRW", "product_revision": "1.00", "vendor_specific": "N0A13A1", "firmware_date": "211303221728" @@ -2303,6 +2408,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N000500", "firmware_date": "211512230930" @@ -2323,6 +2429,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "AS00", "vendor_specific": "U008408", "firmware_date": "211607291602" @@ -2343,6 +2450,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N002602", "firmware_date": "211409031732" @@ -2363,6 +2471,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N002802", "firmware_date": "211702151828" @@ -2383,6 +2492,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N005305", "firmware_date": "211504211939" @@ -2403,6 +2513,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N000800", "firmware_date": "211701031453" @@ -2423,6 +2534,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BDDVDRW", "product_revision": "1.00", "vendor_specific": "N000200", "firmware_date": "211602231042" @@ -2443,6 +2555,7 @@ { "identity": { "vendor_id": "ASUS", + "product_id": "BC-12B1ST", "product_revision": "3.00", "vendor_specific": "W004704", "firmware_date": "211512151340" @@ -2463,6 +2576,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.02", "vendor_specific": "N1A12A1", "firmware_date": "211304042322" @@ -2483,6 +2597,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "A100", "vendor_specific": "D00D60D", "firmware_date": "211507301534" @@ -2503,6 +2618,7 @@ { "identity": { "vendor_id": "hp", + "product_id": "BDRW", "product_revision": "BW20", "vendor_specific": "P017417", "firmware_date": "211412160945" @@ -2523,6 +2639,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.02", "vendor_specific": "N000200", "firmware_date": "211512111436" @@ -2543,6 +2660,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.01", "vendor_specific": "N001601", "firmware_date": "211406161935" @@ -2563,6 +2681,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BDDVDRW", "product_revision": "1.01", "vendor_specific": "N0A02A0", "firmware_date": "211312061614" @@ -2583,6 +2702,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1514", "vendor_specific": "N100400", "firmware_date": "211205301654" @@ -2603,6 +2723,7 @@ { "identity": { "vendor_id": "ASUS", + "product_id": "BW-16D1H-U", "product_revision": "A101", "vendor_specific": "W000600", "firmware_date": "211412080959" @@ -2623,6 +2744,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "FR07", "vendor_specific": "J001101", "firmware_date": "211612201651" @@ -2643,6 +2765,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.02", "vendor_specific": "N0A02A0", "firmware_date": "211312061545" @@ -2663,6 +2786,7 @@ { "identity": { "vendor_id": "ASUS", + "product_id": "BW-16D1HT", "product_revision": "3.02", "vendor_specific": "W000800", "firmware_date": "211711241413" @@ -2683,6 +2807,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "CCT2", "vendor_specific": "V000700", "firmware_date": "211703071420" @@ -2703,6 +2828,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.02", "vendor_specific": "N000100", "firmware_date": "211512111503" @@ -2723,6 +2849,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N003103", "firmware_date": "211612201528" @@ -2743,6 +2870,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.01", "vendor_specific": "N1A12A1", "firmware_date": "211304042325" @@ -2763,6 +2891,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N1A44A4", "firmware_date": "211206161825" @@ -2783,6 +2912,7 @@ { "identity": { "vendor_id": "ASUS", + "product_id": "BW-16D1HT", "product_revision": "3.00", "vendor_specific": "W004504", "firmware_date": "211508101633" @@ -2803,6 +2933,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.01", "vendor_specific": "N003803", "firmware_date": "211406251441" @@ -2823,6 +2954,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "T.00", "vendor_specific": "V003403", "firmware_date": "211606241658" @@ -2843,6 +2975,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.02", "vendor_specific": "N0A00A0", "firmware_date": "211405071026" @@ -2863,6 +2996,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "A101", "vendor_specific": "D000100", "firmware_date": "211602241144" @@ -2883,6 +3017,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.02", "vendor_specific": "N001401", "firmware_date": "211703101650" @@ -2903,6 +3038,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N000500", "firmware_date": "211704251756" @@ -2923,6 +3059,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N000700", "firmware_date": "211701030941" @@ -2943,6 +3080,7 @@ { "identity": { "vendor_id": "hp", + "product_id": "BD-RE", "product_revision": "B7C6", "vendor_specific": "P0A12A1", "firmware_date": "211303262313" @@ -2963,6 +3101,7 @@ { "identity": { "vendor_id": "hp", + "product_id": "BDDVDRW", "product_revision": "BC56", "vendor_specific": "P0B00B0", "firmware_date": "210000000000" @@ -2983,6 +3122,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N1A26A2", "firmware_date": "211202091126" @@ -3003,6 +3143,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.01", "vendor_specific": "N000500", "firmware_date": "211511051001" @@ -3023,6 +3164,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.01", "vendor_specific": "N000900", "firmware_date": "211606090920" @@ -3043,6 +3185,7 @@ { "identity": { "vendor_id": "hp", + "product_id": "BD-RE", "product_revision": "BU24", "vendor_specific": "P005005", "firmware_date": "211504221344" @@ -3063,6 +3206,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N001401", "firmware_date": "211508180940" @@ -3083,6 +3227,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "A1B0", "vendor_specific": "D015815", "firmware_date": "211703101705" @@ -3103,6 +3248,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N006206", "firmware_date": "211512221501" @@ -3123,6 +3269,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "R002202", "firmware_date": "211609090954" @@ -3143,6 +3290,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N001501", "firmware_date": "211504221546" @@ -3163,6 +3311,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N000400", "firmware_date": "211502161437" @@ -3183,6 +3332,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.01", "vendor_specific": "N1A03A0", "firmware_date": "210000000000" @@ -3203,6 +3353,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BDDVDRW", "product_revision": "1.00", "vendor_specific": "N0A13A1", "firmware_date": "211303221725" @@ -3223,6 +3374,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "A100", "vendor_specific": "D032232", "firmware_date": "211701021159" @@ -3243,6 +3395,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.02", "vendor_specific": "N000200", "firmware_date": "211512111440" @@ -3263,6 +3416,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "EB02", "vendor_specific": "C000400", "firmware_date": "211610101049" @@ -3283,6 +3437,7 @@ { "identity": { "vendor_id": "ASUS", + "product_id": "BC-12D2HT", "product_revision": "3.00", "vendor_specific": "W004704", "firmware_date": "211512091147" @@ -3303,6 +3458,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "TSTQ", "vendor_specific": "N000500", "firmware_date": "211508121144" @@ -3323,6 +3479,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N004204", "firmware_date": "211512221504" @@ -3343,6 +3500,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N002802", "firmware_date": "211702151837" @@ -3363,6 +3521,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.03", "vendor_specific": "N0A09A0", "firmware_date": "211405071012" @@ -3383,6 +3542,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N000800", "firmware_date": "211603171336" @@ -3403,6 +3563,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.01", "vendor_specific": "N0A02A0", "firmware_date": "211312061658" @@ -3423,6 +3584,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.01", "vendor_specific": "N005905", "firmware_date": "211408221631" @@ -3443,6 +3605,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "T.02", "vendor_specific": "V001301", "firmware_date": "211405091428" @@ -3463,6 +3626,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "GE02", "vendor_specific": "V000700", "firmware_date": "211705182030" @@ -3483,6 +3647,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N1A12A1", "firmware_date": "211304041441" @@ -3503,6 +3668,7 @@ { "identity": { "vendor_id": "ASUS", + "product_id": "BW-16D1HT", "product_revision": "3.01", "vendor_specific": "W000800", "firmware_date": "211703311426" @@ -3523,6 +3689,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BDDVDRW", "product_revision": "1.00", "vendor_specific": "N002202", "firmware_date": "211602231047" @@ -3543,6 +3710,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N1A44A4", "firmware_date": "211206161828" @@ -3563,6 +3731,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "A100", "vendor_specific": "D031531", "firmware_date": "211608041137" @@ -3583,6 +3752,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "TST5", "vendor_specific": "N000900", "firmware_date": "211708221604" @@ -3603,6 +3773,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "A101", "vendor_specific": "D000700", "firmware_date": "211703131630" @@ -3623,6 +3794,7 @@ { "identity": { "vendor_id": "hp", + "product_id": "BDDVDRW", "product_revision": "B1C0", "vendor_specific": "P015115", "firmware_date": "211307031530" @@ -3643,6 +3815,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "GP01", "vendor_specific": "V001701", "firmware_date": "211504221450" @@ -3663,6 +3836,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N1A11A1", "firmware_date": "211201171401" @@ -3683,6 +3857,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BDDVDRW", "product_revision": "1.00", "vendor_specific": "N0A20A2", "firmware_date": "211306131457" @@ -3703,6 +3878,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "DVDRWBD", "product_revision": "A101", "vendor_specific": "D000300", "firmware_date": "210000000000" @@ -3723,6 +3899,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.02", "vendor_specific": "N001901", "firmware_date": "211703101637" @@ -3743,6 +3920,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N1A44A4", "firmware_date": "211206161823" @@ -3763,6 +3941,7 @@ { "identity": { "vendor_id": "ASUS", + "product_id": "BW-16D1H-U", "product_revision": "A201", "vendor_specific": "W000000", "firmware_date": "211601141358" @@ -3783,6 +3962,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.02", "vendor_specific": "N0A02A0", "firmware_date": "211312061523" @@ -3803,6 +3983,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "G002202", "firmware_date": "211405090917" @@ -3823,6 +4004,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N1A37A3", "firmware_date": "211205021346" @@ -3843,6 +4025,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.03", "vendor_specific": "N0A09A0", "firmware_date": "211403261105" @@ -3863,6 +4046,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.03", "vendor_specific": "N0C06C0", "firmware_date": "211505261714" @@ -3883,6 +4067,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.02", "vendor_specific": "N001401", "firmware_date": "211703101655" @@ -3903,6 +4088,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "AS00", "vendor_specific": "U00B50B", "firmware_date": "211608041424" @@ -3923,6 +4109,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "EB02", "vendor_specific": "C000900", "firmware_date": "211412181002" @@ -3943,6 +4130,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BDDVDRW", "product_revision": "1.00", "vendor_specific": "N004904", "firmware_date": "211409041512" @@ -3963,6 +4151,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N001401", "firmware_date": "211508180932" @@ -3983,6 +4172,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.02", "vendor_specific": "N0A02A0", "firmware_date": "211312061453" @@ -4003,6 +4193,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "G004504", "firmware_date": "211412160950" @@ -4023,6 +4214,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N005405", "firmware_date": "211703101612" @@ -4043,6 +4235,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BDDVDRW", "product_revision": "1.03", "vendor_specific": "N0A05A0", "firmware_date": "211404241900" @@ -4063,6 +4256,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N000600", "firmware_date": "211508191151" @@ -4083,6 +4277,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BDDVDRW", "product_revision": "1.00", "vendor_specific": "N007307", "firmware_date": "211412161028" @@ -4103,6 +4298,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BDDVDRW", "product_revision": "1.02", "vendor_specific": "N0A04A0", "firmware_date": "211403261141" @@ -4123,6 +4319,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.00", "vendor_specific": "N002302", "firmware_date": "211512071657" @@ -4143,6 +4340,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "0217", "vendor_specific": "N000700", "firmware_date": "211603081656" @@ -4163,6 +4361,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BDDVDRW", "product_revision": "1.01", "vendor_specific": "N0A02A0", "firmware_date": "211312061625" @@ -4183,6 +4382,7 @@ { "identity": { "vendor_id": "ASUS", + "product_id": "BW-16D1HT", "product_revision": "3.00", "vendor_specific": "W006706", "firmware_date": "211511031110" @@ -4203,6 +4403,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "CC01", "vendor_specific": "V000700", "firmware_date": "211706280934" @@ -4223,6 +4424,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "A1B0", "vendor_specific": "D014214", "firmware_date": "211510131446" @@ -4243,6 +4445,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.01", "vendor_specific": "N000000", "firmware_date": "211706291017" @@ -4263,6 +4466,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.02", "vendor_specific": "N000100", "firmware_date": "211512111459" @@ -4283,6 +4487,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "1.03", "vendor_specific": "N0C08C0", "firmware_date": "211505261653" @@ -4303,6 +4508,7 @@ { "identity": { "vendor_id": "HL-DT-ST", + "product_id": "BD-RE", "product_revision": "GS01", "vendor_specific": "V001701", "firmware_date": "211305020921" @@ -4322,4 +4528,4 @@ } ], "renesas": [] -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index fd52f9f..a90730c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,9 @@ mod css; // [`all_unlockers`]. `aacs` and `css` carry no such public catalog, so they // stay fully private. pub mod ld; +// `renesis` is public for its `is_renesas` drive-probe; the unlocker impl +// (`Renesis`) is `pub(crate)` — reached only through [`all_unlockers`]. +pub mod renesis; use scsi::ScsiTransport; @@ -29,6 +32,7 @@ use scsi::ScsiTransport; #[derive(Debug, Clone, Default)] pub struct DriveId { pub vendor_id: String, + pub product_id: String, pub product_revision: String, pub vendor_specific: String, pub firmware_date: String, @@ -111,22 +115,39 @@ pub enum UnlockError { /// here — that is the consumer's concern, not bus removal. pub trait Unlocker: Send + Sync { /// Short, stable identifier for this unlocker (e.g. "LibreDrive", "AACS", - /// "CSS"). The ONE place a name lives — apps render the unlocker report from - /// [`all_unlockers`], never hardcoding names, so adding/removing an unlocker - /// updates every report with no app change. + /// "CSS", "Renesas"). The ONE place a name lives — apps render the unlocker + /// report from [`all_unlockers`], never hardcoding names, so adding/removing + /// an unlocker updates every report with no app change. fn name(&self) -> &'static str; - /// True if this unlocker applies to the given context (drive id + disc kind) - /// during live dispatch — the gate [`crate::all_unlockers`] uses to decide - /// whether to RUN it. - fn matches(&self, ctx: &UnlockCtx) -> bool; - - /// Remove the bus-encryption barrier, returning what was learned. - fn unlock( + /// Unlock DRIVE FEATURES — riplock/speed, OEM Volume ID, OEM extended-access + /// reads. The consumer runs this at drive-prep, trying each unlocker until + /// one handles the drive. `Ok(_)` = this unlocker handled it (LibreDrive also + /// removes bus encryption at the drive, so its result carries + /// `drive_unlocked: true`); `Err(NotApplicable)` = not this unlocker's drive; + /// `Err(Transport)` = dead bus (the consumer aborts). Default: not provided. + fn unlock_features( &self, scsi: &mut dyn ScsiTransport, ctx: &UnlockCtx, - ) -> std::result::Result; + ) -> std::result::Result { + let _ = (scsi, ctx); + Err(UnlockError::NotApplicable) + } + + /// Remove BUS ENCRYPTION for the mounted disc (AACS host-cert handshake, or + /// CSS scrambled-sector auth). The consumer runs this only when the bus isn't + /// already clear, trying each unlocker until one handles it. Same `Ok` / + /// `Err(NotApplicable)` / `Err(Transport)` contract as [`unlock_features`]. + /// Default: not provided. + fn unlock_bus( + &self, + scsi: &mut dyn ScsiTransport, + ctx: &UnlockCtx, + ) -> std::result::Result { + let _ = (scsi, ctx); + Err(UnlockError::NotApplicable) + } } /// Name of the unlocker that claims this drive by identity (for drive-info "is @@ -144,6 +165,7 @@ pub fn unlocker_name(drive_id: &DriveId) -> Option<&'static str> { pub fn all_unlockers() -> Vec> { vec![ Box::new(ld::LibreDrive::new()), + Box::new(renesis::Renesis::new()), Box::new(aacs::AacsCert::new()), Box::new(css::CssUnlocker::new()), ] diff --git a/src/renesis.rs b/src/renesis.rs new file mode 100644 index 0000000..4ecb34c --- /dev/null +++ b/src/renesis.rs @@ -0,0 +1,223 @@ +//! renesis — the Renesas-platform unlocker (Pioneer + HL-DT-ST Renesas drives). +//! +//! Optical drives split into two controller families: MediaTek (handled by +//! [`crate::ld`], per-drive firmware) and Renesas. This module owns the Renesas +//! side. Detection is a single vendor probe — a Renesas controller serves the +//! READ_BUFFER 0x02/0xF1 identity block (ASCII `SAT` interface marker at +//! `[16..19]`); a MediaTek drive rejects the command with ILLEGAL REQUEST. See +//! [`is_renesas`]. +//! +//! renesis provides the drive-FEATURES capability ([`Renesis::unlock_features`]) +//! and NOT bus removal (the cert handles the bus). The feature unlock is a no-op +//! for now — it recognizes the drive and reports the match, deferring the bus to +//! the cert stage. + +use crate::scsi::{DataDirection, ScsiTransport}; +use crate::{UnlockCtx, UnlockError, Unlocked, Unlocker}; + +/// READ_BUFFER mode 0x02, buffer 0xF1 — the Renesas vendor identity buffer. +const RB_F1_CDB: [u8; 10] = [0x3C, 0x02, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00]; +const RB_F1_LEN: usize = 48; +/// The ASCII interface marker a Renesas controller returns at `[16..19]`. +const RENESAS_MARKER: &[u8] = b"SAT"; +const RENESAS_MARKER_OFFSET: usize = 16; + +/// Renesas feature-unlock command (RS8xxx+ platforms). A single fixed vendor +/// WRITE BUFFER (opcode `0x3B`, mode `0x02`, buffer id `0x41`): issuing it +/// authenticates the host and enables the drive's extended feature set. +/// +/// Platform-invariant — the same command unlocks every supported Renesas +/// firmware; it does not vary per drive or per firmware revision. Sent by +/// [`Renesis::unlock_features`]. (Verify on hardware before relying on it.) +#[allow(dead_code)] +const RENESAS_CHALLENGE_CDB: [u8; 10] = + [0x3B, 0x02, 0x41, 0xA5, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00]; + +/// True if `scsi` is a Renesas-platform drive (Pioneer or HL-DT-ST Renesas). +/// +/// Issues the vendor READ_BUFFER 0x02/0xF1 probe: a Renesas controller serves a +/// 48-byte identity block whose bytes `[16..19]` are the ASCII `SAT` interface +/// tag. A MediaTek drive rejects it (ILLEGAL REQUEST → `Err`), and a transport +/// fault also yields `Err`; both return `false`. This is the definitive +/// Renesas-vs-MediaTek split. +pub fn is_renesas(scsi: &mut dyn ScsiTransport) -> bool { + let mut buf = [0u8; RB_F1_LEN]; + match scsi.execute(&RB_F1_CDB, DataDirection::FromDevice, &mut buf, 5_000) { + Ok(r) => { + let end = RENESAS_MARKER_OFFSET + RENESAS_MARKER.len(); + r.status == 0 + && r.bytes_transferred >= end + && &buf[RENESAS_MARKER_OFFSET..end] == RENESAS_MARKER + } + Err(_) => false, + } +} + +/// The Renesas-platform unlocker. `pub(crate)` — reached only through +/// [`crate::all_unlockers`]. +pub(crate) struct Renesis; + +impl Renesis { + pub(crate) fn new() -> Self { + Renesis + } +} + +impl Unlocker for Renesis { + fn name(&self) -> &'static str { + "Renesas" + } + + /// `if is_renesas() { recognized }`. Renesas is a distinct platform from + /// LibreDrive (MediaTek): it provides DRIVE FEATURES only — it does NOT remove + /// AACS bus encryption (`unlock_bus` is left at the default, so the cert stage + /// handles the bus). The feature unlock itself is not implemented yet (no-op), + /// but the match IS reported (`Ok`, `drive_unlocked: false`) so the drive is + /// recognized as Renesas. A non-Renesas drive → `NotApplicable`. + fn unlock_features( + &self, + scsi: &mut dyn ScsiTransport, + _ctx: &UnlockCtx, + ) -> std::result::Result { + if !is_renesas(scsi) { + return Err(UnlockError::NotApplicable); + } + // Recognized Renesas drive (Pioneer / HL-DT-ST Renesas). Feature unlock: + // TODO. `drive_unlocked: false` → bus encryption is left for the cert. + tracing::debug!( + target: "freemkv::disc", + phase = "renesas_recognized", + "Renesas drive recognized; feature unlock TODO, bus deferred to cert" + ); + Ok(Unlocked { + vid: None, + bus_key: None, + drive_unlocked: false, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::DiscKind; + use crate::scsi::{DataDirection, Result, ScsiError, ScsiResult, ScsiTransport}; + + /// Serves a fixed READ_BUFFER payload (Renesas-like) with a Good status. + struct RenesasTransport { + payload: Vec, + } + impl ScsiTransport for RenesasTransport { + fn execute( + &mut self, + _cdb: &[u8], + _dir: DataDirection, + data: &mut [u8], + _timeout_ms: u32, + ) -> Result { + let n = self.payload.len().min(data.len()); + data[..n].copy_from_slice(&self.payload[..n]); + Ok(ScsiResult { + status: 0, + bytes_transferred: n, + sense: [0u8; 32], + }) + } + } + + /// Rejects the command (a MediaTek drive → ILLEGAL REQUEST → transport `Err`). + struct RejectingTransport; + impl ScsiTransport for RejectingTransport { + fn execute( + &mut self, + _cdb: &[u8], + _dir: DataDirection, + _data: &mut [u8], + _timeout_ms: u32, + ) -> Result { + Err(ScsiError { + status: crate::scsi::SCSI_STATUS_CHECK_CONDITION, + sense: None, + }) + } + } + + fn renesas_payload() -> Vec { + // 48-byte RB 0xF1 block with "SAT" at [16..19] (the real S13JX shape). + let mut p = vec![0x20u8; 48]; + p[16..19].copy_from_slice(b"SAT"); + p + } + + #[test] + fn is_renesas_true_on_sat_marker() { + let mut t = RenesasTransport { + payload: renesas_payload(), + }; + assert!(is_renesas(&mut t)); + } + + #[test] + fn is_renesas_false_when_command_rejected() { + let mut t = RejectingTransport; + assert!(!is_renesas(&mut t)); + } + + #[test] + fn is_renesas_false_on_missing_marker() { + // Good status but no "SAT" at [16..19] (e.g. a stray buffer). + let mut t = RenesasTransport { + payload: vec![0u8; 48], + }; + assert!(!is_renesas(&mut t)); + } + + #[test] + fn is_renesas_false_on_short_response() { + // Fewer than 19 bytes returned — can't carry the marker. + let mut t = RenesasTransport { + payload: vec![0x20u8; 8], + }; + assert!(!is_renesas(&mut t)); + } + + #[test] + fn features_report_match_without_bus_removal_on_renesas() { + let mut t = RenesasTransport { + payload: renesas_payload(), + }; + let id = crate::DriveId::default(); + let ctx = UnlockCtx::new(&id, DiscKind::Unknown, &[]); + // Recognized → Ok, but a feature-only unlock: bus NOT removed, no VID. + let u = Renesis::new() + .unlock_features(&mut t, &ctx) + .expect("renesas → Ok"); + assert!( + !u.drive_unlocked, + "renesis does not remove the bus (cert does)" + ); + assert_eq!(u.vid, None); + assert_eq!(u.bus_key, None); + } + + #[test] + fn features_not_applicable_on_non_renesas() { + let mut t = RejectingTransport; + let id = crate::DriveId::default(); + let ctx = UnlockCtx::new(&id, DiscKind::Unknown, &[]); + let err = Renesis::new().unlock_features(&mut t, &ctx).unwrap_err(); + assert_eq!(err, UnlockError::NotApplicable); + } + + #[test] + fn does_not_provide_bus_removal() { + // Renesas leaves bus encryption to the cert: unlock_bus is the default. + let mut t = RenesasTransport { + payload: renesas_payload(), + }; + let id = crate::DriveId::default(); + let ctx = UnlockCtx::new(&id, DiscKind::Aacs, &[]); + let err = Renesis::new().unlock_bus(&mut t, &ctx).unwrap_err(); + assert_eq!(err, UnlockError::NotApplicable); + } +}