Files
libfreemkv/docs/architecture.md
T
MattJackson 272215c551 Clean up for public release: docs, zero warnings, no hardcoded paths
Documentation:
- docs/aacs.md — AACS encryption (1.0 + 2.0), key resolution, decrypt
- docs/udf.md — UDF 2.50 filesystem with metadata partitions
- docs/mpls.md — MPLS playlist format, STN stream table
- docs/clpi.md — CLPI clip info, EP map, sector extents
- docs/architecture.md — library module map, design principles
- docs/drive-access.md — drive sessions, SCSI transport, unlock

Code cleanup:
- Zero compiler warnings
- Removed all debug eprintln from library code
- No hardcoded private paths — KEYDB tests use KEYDB_PATH env var
- KEYDB search locations as named constants
- drive.rs: extracted create_platform(), deduplicated open methods
- lib.rs: updated doc examples to show Disc::scan() API
- Fixed UDF file reads (partition_start, not metadata_start)
- Exported KeySource from disc module
2026-04-07 12:12:24 -07:00

6.6 KiB

libfreemkv Architecture

Open source optical drive access library for 4K UHD Blu-ray, Blu-ray, and DVD. Rust library with no external dependencies at runtime -- profiles are bundled, AACS keys are derived internally, and all SCSI communication is handled in-process.

Repository: https://github.com/freemkv/libfreemkv License: AGPL-3.0-only


Design Principles

  1. CLI is dumb. All drive communication, disc parsing, AACS decryption, and format handling live in the library. CLI binaries are thin wrappers that call DriveSession::open() and Disc::scan().

  2. No external files. 206 drive profiles are compiled into the binary via include_str!. No configuration directory, no runtime file lookups for drive support.

  3. Transparent AACS. The ContentReader decrypts on the fly when keys are available. Callers read cleartext sectors without knowing whether the disc was encrypted.

  4. Structured errors, no English. Every error has a numeric code (E1000-E7000). The library never formats user-facing messages -- applications do that.

  5. Library-agnostic. No concept of "supported" vs "unsupported" drives at a policy level. If a profile exists, the library uses it.


Module Map

libfreemkv (lib.rs)
│
├── Drive Access
│   ├── drive         DriveSession — open, identify, unlock, read
│   ├── scsi          ScsiTransport trait + SG_IO implementation
│   ├── platform/     Platform trait — per-chipset command handlers
│   │   └── mt1959    MediaTek MT1959 driver (LG, ASUS, hp)
│   ├── profile       DriveProfile loading, matching, bundled JSON
│   ├── identity      DriveId from INQUIRY + GET_CONFIG 010C
│   └── speed         DriveSpeed enum, SET CD SPEED CDB builder
│
├── Disc Scanning
│   ├── disc          Disc::scan() — titles, streams, extents, AACS setup
│   ├── udf           UDF 2.50 filesystem reader (metadata partitions)
│   ├── mpls          MPLS playlist parser — clips, streams, STN table
│   ├── clpi          CLPI clip info parser — EP map, sector extents
│   └── jar           BD-J JAR label extraction (audio/subtitle names)
│
├── Encryption
│   ├── aacs          KEYDB parsing, VUK lookup, MKB processing, unit decryption
│   └── aacs_handshake  ECDH bus authentication, Volume ID, Read Data Key
│
└── error             Error enum with numeric codes E1000-E7000

Drive Access Flow

DriveSession::open("/dev/sr0")
  │
  ├─ scsi::open()           Open /dev/sr0 via SG_IO
  ├─ DriveId::from_drive()  INQUIRY + GET_CONFIG 010C
  ├─ profile::find_by_drive_id()  Match against 206 bundled profiles
  ├─ Platform::new()        Instantiate chipset driver (Mt1959)
  └─ Platform::unlock()     Activate raw disc access mode

After open, the session provides:

  • read_sectors(lba, count, buf) -- raw sector reads (through platform driver)
  • read_disc(lba, count, buf) -- standard READ(10) for filesystem data
  • scsi_execute(cdb, dir, buf, timeout) -- arbitrary SCSI commands
  • status(), calibrate(), read_config(), read_register()

Disc Scanning Flow

Disc::scan(&mut session, &ScanOptions)
  │
  ├─ READ CAPACITY          Get disc size in sectors
  ├─ udf::read_filesystem() Parse UDF 2.50 (AVDP → VDS → metadata → FSD → root)
  ├─ For each BDMV/PLAYLIST/*.mpls:
  │   ├─ mpls::parse()      Extract play items, STN streams
  │   └─ For each clip:
  │       └─ clpi::parse()  EP map → sector extents for the clip's time range
  ├─ Detect AACS            Check for /AACS directory on disc
  └─ Disc::setup_aacs()     Handshake + KEYDB → VUK → unit keys (if encrypted)

The result is a Disc with:

  • titles: Vec<Title> -- sorted by duration, each with streams and sector extents
  • aacs: Option<AacsState> -- decryption keys if available
  • encrypted: bool -- whether the disc uses AACS

AACS Decryption

Four key resolution paths, tried in order:

Path Method Speed
1 VUK lookup by disc hash in KEYDB.cfg Instant
2 Media Key + Volume ID from KEYDB → derive VUK Fast
3 Processing Keys + MKB → Media Key → VUK Medium
4 Device Keys + MKB subset-difference tree → VUK Slow

The AACS handshake (aacs_handshake) performs ECDH key agreement over the AACS 1.0 160-bit elliptic curve to obtain:

  • Volume ID -- needed for VUK derivation (paths 2-4)
  • Read Data Key -- needed for AACS 2.0 (UHD) bus decryption

Content decryption uses AES-128-CBC on 6144-byte aligned units. The ContentReader handles this transparently.


Error Codes

All errors carry a numeric code for programmatic handling. No user-facing text is baked into the library.

Range Category Examples
E1xxx Device errors DeviceNotFound, DevicePermission
E2xxx Profile errors UnsupportedDrive, ProfileNotFound, ProfileParse
E3xxx Unlock errors UnlockFailed, SignatureMismatch, NotUnlocked, NotCalibrated
E4xxx SCSI errors ScsiError, ScsiTimeout
E5xxx I/O errors IoError (wraps std::io::Error)
E6xxx Disc format errors DiscError (UDF, MPLS, CLPI parse failures)
E7xxx AACS errors AacsError (key resolution, handshake, decryption)

Platform Support

Platform Transport Status
Linux SG_IO ioctl on /dev/sr* Implemented
macOS IOKit SCSI passthrough Planned
Windows SPTI (IOCTL_SCSI_PASS_THROUGH_DIRECT) Planned

The ScsiTransport trait abstracts the platform. Adding a new platform requires implementing execute() for that OS and wiring it into scsi::open().


Chipset Support

Chipset Drives Status
MediaTek MT1959 LG, ASUS, hp Implemented (206 profiles)
Renesas RS8xxx/RS9xxx Pioneer, some HL-DT-ST Planned

The Platform trait abstracts chipset-specific commands. Each chipset implements 10 handlers (unlock, config, register, calibrate, keepalive, status, probe, read_sectors, timing). All handlers are accessed via SCSI READ BUFFER with chipset-specific mode and buffer ID bytes.


Build

cargo build --release

Linux builds produce a static library and two binaries (freemkv-info, freemkv-test). The libc dependency is Linux-only. On non-Linux platforms, the library compiles but scsi::open() returns a platform-not-supported error until the IOKit/SPTI backends are implemented.