From 4077c2c8179dedf0f4983661184ae66a0ff1a324 Mon Sep 17 00:00:00 2001 From: Matthew Jackson Date: Thu, 21 May 2026 14:43:20 -0700 Subject: [PATCH] v0.25.14: rename Drive raw-read API to remove third-party project breadcrumbs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pure rename pass — no behavioral change: - Drive::is_libredrive_active() → Drive::is_raw_read_active() - PlatformDriver::is_libredrive_active() trait method (same rename) - Mt1959 struct field libredrive_active → raw_read_active - Error::AacsLibredriveUnsupported → Error::AacsRawReadUnsupported (numeric code E7016 unchanged) - All callers, tests, and doc comments updated to the new name. Old identifiers removed entirely; downstream consumers must update. Mirrored in bdemu, freemkv, autorip, freemkv-tools. --- CHANGELOG.md | 15 ++++++++++ Cargo.toml | 2 +- src/aacs/keys.rs | 2 +- src/disc/encrypt.rs | 15 +++++----- src/disc/mod.rs | 4 +-- src/drive/mod.rs | 10 +++---- src/error.rs | 12 ++++---- src/platform/mod.rs | 16 +++++----- src/platform/mt1959/mod.rs | 61 ++++++++++++++++++++------------------ 9 files changed, 77 insertions(+), 60 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80fbf60..31a8353 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 0.25.14 (2026-05-21) + +### Changed + +- `Drive::is_libredrive_active()` renamed to `Drive::is_raw_read_active()`. + Same semantics; old name removed. Mirrored on the internal + `PlatformDriver::is_libredrive_active()` trait method (now + `is_raw_read_active()`). +- `Error::AacsLibredriveUnsupported` renamed to + `Error::AacsRawReadUnsupported`; the underlying numeric code (E7016) + is unchanged. The `E_AACS_LIBREDRIVE_UNSUPPORTED` constant is + renamed to `E_AACS_RAW_READ_UNSUPPORTED`. + +No behavioural change — purely a rename pass. + ## 0.25.13 (2026-05-21) ### Added diff --git a/Cargo.toml b/Cargo.toml index 224c0f0..5c1a449 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libfreemkv" -version = "0.25.13" +version = "0.25.14" edition = "2024" rust-version = "1.86" license = "AGPL-3.0-only" diff --git a/src/aacs/keys.rs b/src/aacs/keys.rs index 4f5cf7e..4698849 100644 --- a/src/aacs/keys.rs +++ b/src/aacs/keys.rs @@ -858,7 +858,7 @@ fn resolve_keys_classical(ctx: &ResolveContext<'_>, version: AacsVersion) -> Opt } // Paths 2-4 all consume the Volume ID. Without it (handshake - // skipped, libredrive bypass failed, etc.) every downstream + // skipped, raw-read bypass failed, etc.) every downstream // derivation produces garbage. Caller stamps `[0u8; 16]` as the // sentinel "no VID" — short-circuit here so we don't surface a // misleading "all paths failed" log when really the math is diff --git a/src/disc/encrypt.rs b/src/disc/encrypt.rs index 8cf902f..497b830 100644 --- a/src/disc/encrypt.rs +++ b/src/disc/encrypt.rs @@ -17,10 +17,10 @@ impl Disc { /// SCSI handshake — AACS mutual auth via host certs from the keydb, /// returning VID (and bus keys when applicable) on success. /// - /// `Drive::is_libredrive_active()` is logged for diagnostics but no + /// `Drive::is_raw_read_active()` is logged for diagnostics but no /// longer alters the auth path. v0.25.11 introduced a "raw-read VID" /// shortcut that issued `READ_DISC_STRUCTURE` format 0x80 with - /// AGID=0 on libredrive-active drives, on the hypothesis that the + /// AGID=0 on raw-read-active drives, on the hypothesis that the /// firmware-uploaded drive would serve VID without cert auth. The /// BU40N returned 0x05/0x6F/0x02 (`KEY NOT ESTABLISHED`) to that /// CDB — the AACS spec requires an AGID established via successful @@ -33,7 +33,7 @@ impl Disc { /// Returns `(handshake, error)`: /// * `(Some(_), None)` — VID acquired /// * `(None, Some(_))` — specific failure mode (see - /// `AacsHostCertRejected` / `AacsLibredriveUnsupported` / + /// `AacsHostCertRejected` / `AacsRawReadUnsupported` / /// `AacsVidUnavailable` variants in `error.rs`) /// * `(None, None)` — handshake not attempted (no keydb; /// resolution will proceed with VID=zero and rely on path 1 @@ -45,7 +45,7 @@ impl Disc { tracing::warn!( target: "freemkv::disc", phase = "handshake_entry", - libredrive_active = session.is_libredrive_active(), + raw_read_active = session.is_raw_read_active(), "do_handshake entered" ); @@ -101,10 +101,9 @@ impl Disc { if host_cert_count == 0 { // No host certs in keydb -> cert auth cannot proceed. - // Surface as - // LibredriveUnsupported so the caller knows neither path - // is available on this configuration. - return (None, Some(Error::AacsLibredriveUnsupported)); + // Surface as RawReadUnsupported so the caller knows + // neither path is available on this configuration. + return (None, Some(Error::AacsRawReadUnsupported)); } // v0.25.7 wedge fix. Pre-0.25.7 this loop fired up to 16 AACS diff --git a/src/disc/mod.rs b/src/disc/mod.rs index 2eef184..e475bdf 100644 --- a/src/disc/mod.rs +++ b/src/disc/mod.rs @@ -1044,7 +1044,7 @@ impl Disc { /// All disc reads use standard READ(10) via UDF -- no vendor SCSI commands. pub fn scan(session: &mut Drive, opts: &ScanOptions) -> Result { // AACS handshake (Blu-ray/UHD). Cert-based mutual auth; logs - // is_libredrive_active() as a diagnostic but the auth path no + // is_raw_read_active() as a diagnostic but the auth path no // longer branches on it. let (handshake, handshake_error) = Self::do_handshake(session, opts); @@ -1133,7 +1133,7 @@ impl Disc { /// Core scan pipeline — works with any SectorSource. /// /// `handshake_error` is plumbed from `do_handshake` so failures - /// (cert rejected, libredrive unsupported, VID read failed) are + /// (cert rejected, raw-read unsupported, VID read failed) are /// preserved as `disc.aacs_error` for callers to render. When key /// resolution succeeds despite the handshake failure (built-in /// keys + disc-hash lookup hit) the error is dropped. diff --git a/src/drive/mod.rs b/src/drive/mod.rs index 338a5e6..3f80da4 100644 --- a/src/drive/mod.rs +++ b/src/drive/mod.rs @@ -432,7 +432,7 @@ impl Drive { } } - /// True if the drive is currently in libredrive raw-read mode. + /// True if the drive is currently in raw-read mode. /// /// Detected by the platform driver during `init()` from the unlock /// response's mode markers. When true: @@ -444,11 +444,11 @@ impl Drive { /// /// AACS layer code should branch on this: if true, skip /// `aacs::handshake::aacs_authenticate` (the cert dance) and read - /// VID via the libredrive alternate path. If false, fall back to - /// the standard cert-based handshake. - pub fn is_libredrive_active(&self) -> bool { + /// VID via the alternate VID read path. If false, fall back to the + /// standard cert-based handshake. + pub fn is_raw_read_active(&self) -> bool { match self.driver { - Some(ref d) => d.is_libredrive_active(), + Some(ref d) => d.is_raw_read_active(), None => false, } } diff --git a/src/error.rs b/src/error.rs index 885b754..055ffc6 100644 --- a/src/error.rs +++ b/src/error.rs @@ -72,7 +72,7 @@ pub const E_AACS_DATA_KEY: u16 = 7011; pub const E_DECRYPT_FAILED: u16 = 7013; pub const E_CSS_AUTH_FAILED: u16 = 7014; pub const E_AACS_HOST_CERT_REJECTED: u16 = 7015; -pub const E_AACS_LIBREDRIVE_UNSUPPORTED: u16 = 7016; +pub const E_AACS_RAW_READ_UNSUPPORTED: u16 = 7016; pub const E_AACS_VID_UNAVAILABLE: u16 = 7017; pub const E_AACS_MK_UNAVAILABLE: u16 = 7018; pub const E_AACS_VUK_NOT_IN_KEYDB: u16 = 7019; @@ -230,11 +230,11 @@ pub enum Error { /// Host certificate rejected by the drive's revocation list (HRL hit). /// All available host certs failed mutual auth on this drive. AacsHostCertRejected, - /// Drive cannot be put into libredrive raw-read mode and standard - /// AACS cert auth failed. No path to decryption remains. - AacsLibredriveUnsupported, + /// Drive cannot be put into raw-read mode and standard AACS cert + /// auth failed. No path to decryption remains. + AacsRawReadUnsupported, /// Volume ID could not be retrieved from the drive (neither via cert - /// auth nor via the libredrive alternate path). Downstream of step 1 + /// auth nor via the alternate VID read path). Downstream of step 1 /// of the AACS chain. AacsVidUnavailable, /// No available path produced a Media Key (no MK+VID in keydb, no @@ -329,7 +329,7 @@ impl Error { Error::DecryptFailed => E_DECRYPT_FAILED, Error::CssAuthFailed => E_CSS_AUTH_FAILED, Error::AacsHostCertRejected => E_AACS_HOST_CERT_REJECTED, - Error::AacsLibredriveUnsupported => E_AACS_LIBREDRIVE_UNSUPPORTED, + Error::AacsRawReadUnsupported => E_AACS_RAW_READ_UNSUPPORTED, Error::AacsVidUnavailable => E_AACS_VID_UNAVAILABLE, Error::AacsMkUnavailable => E_AACS_MK_UNAVAILABLE, Error::AacsVukNotInKeydb => E_AACS_VUK_NOT_IN_KEYDB, diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 572e7fe..80665db 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -19,17 +19,17 @@ pub(crate) trait PlatformDriver: Send { /// True after successful init(). fn is_ready(&self) -> bool; - /// True if the drive is currently in libredrive raw-read mode (the - /// per-drive runtime firmware has been uploaded AND the drive - /// confirms active mode via the `MMkv` / `LbDr` markers in the - /// unlock response). When true the host can read sectors without - /// AACS bus encryption and retrieve VID without cert-based mutual - /// auth — the cert/HRL gate on the drive's standard AACS path is - /// effectively bypassed by the alternate data path. + /// True if the drive is currently in raw-read mode (the per-drive + /// runtime firmware has been uploaded AND the drive confirms + /// active mode via the marker bytes in the unlock response). When + /// true the host can read sectors without AACS bus encryption and + /// retrieve VID without cert-based mutual auth — the cert/HRL gate + /// on the drive's standard AACS path is effectively bypassed by + /// the alternate data path. /// /// Default `false` — platforms that don't implement this mode are /// always reported as inactive. - fn is_libredrive_active(&self) -> bool { + fn is_raw_read_active(&self) -> bool { false } } diff --git a/src/platform/mt1959/mod.rs b/src/platform/mt1959/mod.rs index cd85f98..2d17f8b 100644 --- a/src/platform/mt1959/mod.rs +++ b/src/platform/mt1959/mod.rs @@ -52,10 +52,10 @@ pub struct Mt1959 { pub(crate) buffer_id: u8, pub(crate) unlocked: bool, /// True when the unlock response carried both the per-drive - /// signature AND the active-mode markers (`MMkv` at [12..16], - /// `LbDr` at [16..20]). When true the drive will accept raw-read - /// SCSI traffic without AACS bus encryption / cert auth. - libredrive_active: bool, + /// signature AND a 4-byte marker at offset 12 plus a secondary + /// 4-byte marker at offset 16. When true the drive will accept + /// raw-read SCSI traffic without AACS bus encryption / cert auth. + raw_read_active: bool, probed: bool, } @@ -71,7 +71,7 @@ impl Mt1959 { mode, buffer_id, unlocked: false, - libredrive_active: false, + raw_read_active: false, probed: false, } } @@ -152,14 +152,15 @@ impl Mt1959 { } // Raw-read mode is active when BOTH the per-drive signature - // matched AND the response carries the secondary `LbDr` marker - // repeated through bytes 16..64. The active-mode signature at - // [12..16] checked above is the primary gate; the [16..20] - // marker is the redundant confirmation Mt1959 firmware writes - // through the rest of the response. Requiring both before we - // tell the AACS layer "skip the cert dance" keeps any partial - // / corrupted response from steering us into the bypass. - self.libredrive_active = response.len() >= FIRMWARE_MODE_OFFSET + 4 + // matched AND the response carries the secondary 4-byte marker + // at offset 16, repeated through bytes 16..64. The active-mode + // signature at [12..16] checked above is the primary gate; the + // [16..20] marker is the redundant confirmation the firmware + // writes through the rest of the response. Requiring both + // before we tell the AACS layer "skip the cert dance" keeps + // any partial / corrupted response from steering us into the + // bypass. + self.raw_read_active = response.len() >= FIRMWARE_MODE_OFFSET + 4 && response[FIRMWARE_ACTIVE_OFFSET..FIRMWARE_ACTIVE_OFFSET + 4] == FIRMWARE_ACTIVE_SIG && response[FIRMWARE_MODE_OFFSET..FIRMWARE_MODE_OFFSET + 4] == FIRMWARE_MODE_SIG; @@ -357,8 +358,8 @@ impl PlatformDriver for Mt1959 { self.unlocked } - fn is_libredrive_active(&self) -> bool { - self.libredrive_active + fn is_raw_read_active(&self) -> bool { + self.raw_read_active } } @@ -417,14 +418,15 @@ mod tests { r[0..4].copy_from_slice(&signature); // bytes [4..12] left as zeros (version + reserved per format) r[12..16].copy_from_slice(&mode_marker); - // Real firmware repeats LbDr through [16..64]; the parser only - // checks [16..20], so we just write the marker once. + // Real firmware repeats the secondary marker through [16..64]; + // the parser only checks [16..20], so we just write the marker + // once. r[16..20].copy_from_slice(&id_marker); r } #[test] - fn do_unlock_sets_libredrive_active_when_both_markers_present() { + fn do_unlock_sets_raw_read_active_when_both_markers_present() { let sig = [0x99, 0x9E, 0xC3, 0x75]; let response = build_response(sig, FIRMWARE_ACTIVE_SIG, FIRMWARE_MODE_SIG); let mut transport = ScriptedTransport { response }; @@ -434,16 +436,16 @@ mod tests { assert_eq!(raw.len(), 64); assert!(mt.unlocked, "unlocked flag set after success"); assert!( - mt.is_libredrive_active(), - "both MMkv and LbDr present -> libredrive_active" + mt.is_raw_read_active(), + "both markers present -> raw_read_active" ); } #[test] - fn do_unlock_unlocked_but_not_libredrive_when_id_marker_missing() { + fn do_unlock_unlocked_but_not_raw_read_when_id_marker_missing() { // Active-mode primary marker present (so unlock passes) but the - // secondary LbDr marker is replaced with zeros — drive isn't - // serving raw-read traffic on this path. + // secondary marker is replaced with zeros — drive isn't serving + // raw-read traffic on this path. let sig = [0x99, 0x9E, 0xC3, 0x75]; let response = build_response(sig, FIRMWARE_ACTIVE_SIG, [0u8; 4]); let mut transport = ScriptedTransport { response }; @@ -452,8 +454,8 @@ mod tests { mt.do_unlock(&mut transport).expect("unlock should succeed"); assert!(mt.unlocked); assert!( - !mt.is_libredrive_active(), - "missing LbDr marker -> raw-read not active" + !mt.is_raw_read_active(), + "missing secondary marker -> raw-read not active" ); } @@ -470,13 +472,14 @@ mod tests { let err = mt.do_unlock(&mut transport).unwrap_err(); assert!(matches!(err, Error::SignatureMismatch { .. })); assert!(!mt.unlocked); - assert!(!mt.is_libredrive_active()); + assert!(!mt.is_raw_read_active()); } #[test] fn do_unlock_rejects_inactive_mode_marker() { - // Signature matches but [12..16] is NOT MMkv -> drive is not in - // active mode; both unlock and libredrive flag must stay false. + // Signature matches but the primary marker at [12..16] is + // missing -> drive is not in active mode; both unlock and the + // raw-read flag must stay false. let sig = [0x99, 0x9E, 0xC3, 0x75]; let response = build_response(sig, [0u8; 4], FIRMWARE_MODE_SIG); let mut transport = ScriptedTransport { response }; @@ -485,6 +488,6 @@ mod tests { let err = mt.do_unlock(&mut transport).unwrap_err(); assert!(matches!(err, Error::UnlockFailed)); assert!(!mt.unlocked); - assert!(!mt.is_libredrive_active()); + assert!(!mt.is_raw_read_active()); } }