Files
freemkv-unlock/src/ld/cdb.rs
T
Matthew Jackson 314c365290 collapse freemkv-unlock into one crate; ld becomes a module
Replace the ld/aacs/css member-crate workspace with a single freemkv-unlock
crate: the generic Unlocker contract + SCSI transport contract at the crate
root (lib.rs, scsi.rs, error.rs), and the firmware unlocker as the self-
contained src/ld module. The contract is repo-agnostic raw types (DriveId,
HostCert, DiscKind, Unlocked, UnlockError) with NO libfreemkv dependency, so
libfreemkv will depend on this crate (one-way, no cycle) and dispatch via
all_unlockers(). ld now impl crate::Unlocker, takes its own DriveId (4 raw
fields, no INQUIRY parsing), and returns a raw Option<[u8;16]> VID. The old
aacs/css plugin wrappers are removed; their crypto moves in from libfreemkv in
the next stages. 31 tests pass.
2026-06-29 19:11:21 -07:00

58 lines
2.1 KiB
Rust

//! Public unlock-CDB seam.
//!
//! These items expose the *minimum* an external consumer (e.g. the bdemu
//! drive emulator) needs to recognise and answer the LibreDrive unlock
//! READ_BUFFER handshake, without that consumer open-coding the
//! handshake internals. The concrete CDB shapes and the verification
//! marker are unlock-handshake details and must live ONLY in this crate.
/// The 4-byte marker the unlock READ_BUFFER response carries at bytes
/// `[12..16]`. A consumer answering the handshake writes this at that
/// offset; a verifier checks for it there. It is the universal
/// "this is a real unlock reply" tag, independent of the per-drive
/// signature at `[0..4]`.
pub const UNLOCK_MARKER: &[u8] = b"MMkv";
/// Returns `true` when a READ_BUFFER (`0x3C`) CDB with the given mode
/// (`cdb[1] & 0x1F`) and buffer id (`cdb[2]`) is an unlock-handshake
/// read — i.e. one of the LibreDrive unlock variants.
///
/// This is the single source of truth for the unlock READ_BUFFER CDB
/// shapes; consumers must call it rather than hardcoding the mode /
/// buffer-id pairs.
pub fn is_unlock_read_buffer(mode: u8, buf_id: u8) -> bool {
matches!((mode, buf_id), (1, 0x44) | (2, 0x77))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn marker_is_four_bytes() {
assert_eq!(UNLOCK_MARKER, b"MMkv");
assert_eq!(UNLOCK_MARKER.len(), 4);
}
#[test]
fn unlock_variants_match() {
// Variant A and variant B.
assert!(is_unlock_read_buffer(1, 0x44));
assert!(is_unlock_read_buffer(2, 0x77));
}
#[test]
fn non_unlock_cdbs_do_not_match() {
// Right mode, wrong buffer id.
assert!(!is_unlock_read_buffer(1, 0x77));
assert!(!is_unlock_read_buffer(2, 0x44));
// Wrong mode, right buffer id.
assert!(!is_unlock_read_buffer(0, 0x77));
assert!(!is_unlock_read_buffer(0, 0x44));
assert!(!is_unlock_read_buffer(3, 0x77));
// Ordinary data reads.
assert!(!is_unlock_read_buffer(2, 0x00));
assert!(!is_unlock_read_buffer(0, 0x00));
}
}