Commit Graph
289 Commits
Author SHA1 Message Date
MattJackson 0270cf6df3 Fix handshake returning fake success on failure
Previously returned HandshakeResult with zeros when all host certs
failed. Now returns None. Also propagates volume_id read failure
instead of silently using zeros.
2026-04-15 01:54:58 +00:00
MattJackson 6824f430d8 decrypt_sectors returns Result — fail instead of silent corruption
Previously used all-zeros AES key when unit_key_idx was out of range,
producing silently corrupted output. Now returns DecryptFailed error.
Also wired --raw flag through InputOptions → set_raw() on streams.
2026-04-15 01:52:36 +00:00
MattJackson 119d09ed02 Wire --raw through InputOptions to streams
set_raw() on IsoStream and DiscStream sets keys to None.
open_input passes raw flag to streams via InputOptions.
Streams skip decrypt when raw=true.
2026-04-15 01:38:18 +00:00
MattJackson 31ba60b66f Decrypt back in streams — streams handle their own decryption
IsoStream decrypts in its read path using keys from scan.
IOStream trait gets keys() method with default DecryptKeys::None.
Pipeline no longer handles decrypt — just reads decrypted bytes.
2026-04-15 01:34:30 +00:00
MattJackson e82e869aa9 Add keys() to IOStream trait — streams know their own decrypt keys
- IOStream::keys() default returns DecryptKeys::None
- IsoStream and DiscStream override with real AACS/CSS keys
- Pipeline calls input.keys() instead of separate scan_keys()
- Streams are self-contained: read bytes + provide keys
2026-04-14 23:42:14 +00:00
MattJackson cc84f954c2 Drive recovery, reset on open, simplified DiscStream
- SgIoTransport::reset() — open/close/TUR/escalate on every open
- Drive::read() — single read method with error recovery (min speed,
  sleep 30s, retry, phase 1/2/3 escalation)
- Removed read_timeout, read_sectors, read_range — one read() method
- DiscStream simplified — no on_error/on_success/Recovery, delegates
  all error handling to Drive::read()
- IsoStream no longer decrypts — streams return raw bytes, pipeline
  handles decryption
- reset() on all platforms (Linux real, Windows/macOS stubs)
- Watchdog thread removed — kernel handles USB timeouts
2026-04-14 23:32:22 +00:00
MattJackson d51e5c5a7f Bump to 0.9.0 2026-04-13 02:13:15 +00:00
MattJackson 0708262562 Add decrypt module, merge to one drive.read(), Disc::decrypt_keys()
- New decrypt.rs: DecryptKeys enum (AACS/CSS/None) + decrypt_sectors()
- Single drive.read() replaces read_disc/read_content (same SCSI READ(10))
- ContentReader and DiscStream use decrypt_sectors() (no duplicated crypto)
- Disc::decrypt_keys() exposes resolved keys for disc-to-ISO
2026-04-13 02:08:58 +00:00
MattJackson 3e8a3afa31 Add Drive::read_capacity() for raw sector dump 2026-04-13 01:45:32 +00:00
MattJackson 62f0cff5b0 Pin Rust 1.86 MSRV in Cargo.toml and CI workflows 2026-04-13 00:35:35 +00:00
MattJackson fc86491557 Fix is_multiple_of nightly API, bump to 0.8.3
Replace s.len().is_multiple_of(2) with s.len() % 2 != 0 for stable Rust.
This was fixed previously but regressed.
2026-04-13 00:31:54 +00:00
MattJackson 45e0bff182 Fix macOS build: mark MacScsiTransport as Send
IOKit COM interface pointers are Mach port references, safe to send
between threads. The Send bound added in 0.8.1 broke macOS builds.
2026-04-13 00:22:19 +00:00
MattJackson 8cef5260c3 Bump to 0.8.1, make profile module public, fix unused import 2026-04-13 00:17:57 +00:00
MattJackson 5467d8e69e API: Drive object, typed StreamUrl, tray lock/unlock, Send traits
- Rename DriveSession → Drive across entire codebase
- find_drives() returns Vec<Drive>, find_drive() returns Option<Drive>
- resolve_device() now pub(crate) — internal only
- StreamUrl is now a typed enum (Disc, Mkv, M2ts, Iso, Network, Stdio, Null)
  with scheme() and path_str() accessors, replacing struct of Strings
- Add lock_tray() / unlock_tray() for safe disc access during rips
- Improve reset() with eject cycle that clears LibreDrive stuck state
- Add Send bounds to ScsiTransport and PlatformDriver traits
- DiscOptions uses PathBuf instead of String for device/keydb paths
- Update doc example to use new Drive API
2026-04-13 00:13:41 +00:00
MattJackson c3d7a7e867 DriveStatus API + reset() + wait_ready with fallback
- DriveStatus enum: TrayOpen, NoDisc, DiscPresent, NotReady, Unknown
- drive_status(): GET EVENT STATUS NOTIFICATION with TUR fallback
- reset(): PREVENT ALLOW → START STOP → init() escalation
- wait_ready(): tries reset on Illegal Request, falls back to drive_status
- Remaining: standard READ(10) still fails in LibreDrive stuck state
2026-04-12 23:36:16 +00:00
MattJackson 82f361d64d WIP: LibreDrive stuck state detection in wait_ready + scan
- wait_ready: detects MMkv vendor probe when TUR returns Illegal Request
- scan: capacity fallback to 0 when READ CAPACITY fails
- Still needs: re-init to restore standard SCSI commands, or use raw reads for UDF
2026-04-11 22:09:26 +00:00
MattJackson f5fbbf42dd Fix UHD remux: skip DV EL from video_pending, dynamic lookahead
- Secondary video streams (Dolby Vision EL) no longer block codec detection
- Lookahead buffer: 10 MB default, 100 MB for UHD (>15 streams)
- Verified: Dune UHD 84.6 GB remux completes at 101 MB/s
- Verified: V for Vendetta BD 21.3 GB remux completes at 131 MB/s
2026-04-11 21:43:51 +00:00
MattJackson 9d63ff75e9 Doc comments, format string inlining, long literal separators
- Doc comments on DriveSession, find_drives, all Error variants, Result type
- 24 format! strings inlined (clippy pedantic)
- 25 long hex literals with separators (0xFFFFFFFF → 0xFFFF_FFFF)
- README install example updated to 0.8
2026-04-11 21:04:44 +00:00
MattJackson 1c3f300f2f Doc comments on public API, README version fix, format string cleanup 2026-04-11 21:01:16 +00:00
MattJackson 654fda547b Granular SCSI query methods on DriveSession, capture uses them
- get_config_feature(code) → Option<Vec<u8>>
- report_key_rpc_state() → Option<Vec<u8>>
- mode_sense_page(page) → Option<Vec<u8>>
- read_buffer(mode, buf_id, length) → Option<Vec<u8>>

capture.rs now uses these methods — zero raw CDB construction.
CLI info.rs has zero SCSI references.
autorip ejects via library, not shell command.
2026-04-11 20:54:04 +00:00
MattJackson dfe6873f8e v0.8.0: DVD support, 100% codecs, 327 tests, 4 audit rounds clean 2026-04-11 20:35:33 +00:00
MattJackson 47bedb73e9 CSS + AACS cross-validation test vectors
AACS: encrypt with aes crate independently, decrypt with our code, verify match
- 3 tests: unit decrypt, alternate key, bus decrypt
- Uses independent AACS IV constant (not imported from library)

CSS: roundtrip snapshots + Stevenson attack validation
- 4 tests: snapshot regression, multi-key roundtrip, attack validation
- Documents limitation: synthetic sectors may not converge on attack

320+ tests total.
2026-04-11 20:31:12 +00:00
MattJackson 1bd47d5537 Fix all v4 audit findings (22 items)
HIGH: ISO writer multi-extent for >4GB, end-to-end MKV mux test
MEDIUM: AACS cvalue bounds, UV offset, macOS discovery, VC-1 resolution
  from sequence header, HEVC profile flags from SPS, ISO CRC + reserve AVDP
LOW: PS AC3 sub-header, CSS crack first-match break, TrackUID unique,
  AC3 no-sync empty return, --all for iso://, --min warning, dead code removed

320 tests, all passing.
2026-04-11 20:29:00 +00:00
MattJackson 8441b0e9b1 Zero clippy warnings: fix all 32 remaining
- Iterator::find() replaces manual loops (6 sites)
- Index-only loops → iterators (4 sites)
- Identical if-blocks merged
- Box large MkvStream WriteState enum variant
- Vec macro initializers, late init fixes
- Unused fields prefixed with underscore (format spec fields)
- Dead code removed or documented

0 clippy warnings. 319 tests passing.
2026-04-11 19:33:13 +00:00
MattJackson a74d395f68 Audit v3 fixes: all 3 tiers (19 findings)
Tier 1 (compilation + correctness):
- Fix nightly-only is_multiple_of → % 2 != 0 (stable Rust compat)
- Fix parse_sample_rate: check 192 before 96 (was returning wrong rate)
- macOS drive discovery: split unix.rs → linux.rs + macos.rs
- Linux: EACCES returns DevicePermission not DeviceNotFound
- CLI pipe.rs: Ctrl+C signal handler added

Tier 2 (correctness + security):
- MkvStream: reset demuxer after scanning→streaming transition
- Windows SPTI: zero data buffer before ioctl
- AACS cert verification: documented why silently skipped
- KEYDB: HOME + USERPROFILE fallback for Windows
- Library modules: pub(crate) for internal modules
- AACS: explicit re-exports, AES primitives pub(crate)

Tier 3 (performance + polish):
- IsoStream: batch 64-sector reads (was 1 sector at a time)
- DiscStream: buffer swap instead of copy in decrypt_and_buffer
- Vec capacity hints in TS/PS demuxer hot paths
- NetworkStream: TLS warning documented
- Batch rip: per-title progress display
- cargo fmt: 0 violations

319 tests, 0 fmt violations.
2026-04-11 19:24:25 +00:00
MattJackson 43f81e4eae Fix audit v2 criticals: DiscStream read loop, CSS crack, ISO writer
Critical fixes:
- DiscStream: persistent read state (was creating new ContentReader per call)
  Full error recovery, AACS/CSS decryption, extent tracking across reads
- CSS crack: labeled 'outer continue (was targeting wrong loop)
- CSS crack: LFSR0 polynomial fixed to match cipher (shifts 8,1,3,7)
- CSS lfsr: operator precedence clarified in LFSR0 init

Warning fixes:
- ISO writer: UDF tag checksums computed (was zeros)
- DVD extents: saturating_add for overflow safety
- Removed dead fields: HandshakeResult.error, ContentReader.content_format
- Added TODO for ISO long_ad >4GB support

319 tests, 20 clippy warnings remaining.
2026-04-11 19:11:25 +00:00
MattJackson d56dd934bb cargo fmt + clippy --fix: 104 format violations fixed, 8 clippy auto-fixes 2026-04-11 19:10:20 +00:00
MattJackson c91d6e371f Chapters, DVD subtitle palette, MKV track flags, progress total_bytes
Chapters:
- MPLS PlayList marks parsed (mark_type 1 = chapter)
- Chapter struct on DiscTitle (time_secs, name)
- MKV Chapters element with EditionEntry/ChapterAtom per mark
- 3 MPLS mark tests + 2 MKV chapter tests

DVD subtitle palette:
- IFO palette extraction (PGC offset 0xA4, 16 × YCbCr colors)
- YCbCr→RGB conversion for VobSub .idx format
- DvdSubParser codec_private returns formatted palette
- codec_data field on SubtitleStream flows through pipeline
- 5 palette tests (YCbCr conversion, formatting, overflow)

MKV track flags:
- FlagDefault: primary video/audio = 1, secondary = 0
- FlagForced: forced subtitles = 1
- Language: set from stream language code
- Already implemented, verified with 4 new tests

Progress total_bytes:
- IOStream trait: total_bytes() -> Option<u64>
- DiscStream, IsoStream: from disc_title.size_bytes
- M2tsStream, MkvStream: from file metadata on open
- NetworkStream, StdioStream, NullStream: None

316 tests total, all passing.
2026-04-11 17:43:47 +00:00
MattJackson 9cb0c369e4 Add MKV muxer, IsoWriter, disc pipeline, and network tests
- MKV muxer: EBML header, segment, cluster, cues, multi-track, keyframe flags — 6 tests
- MkvStream: BD-TS roundtrip, metadata preservation — 2 tests
- IsoWriter: valid UDF, file size update, custom names, empty content — 4 tests
- Disc pipeline: format detection (UHD/BD/DVD), content format, capacity, duration — 5 tests
- Network: listen/connect roundtrip, metadata flow — 2 tests (ignored for CI)
- Encryption: no AACS dir, no keydb — 2 tests
- 297 tests total, all passing
2026-04-11 17:27:36 +00:00
MattJackson 63accb6718 100% codec coverage + disc/ and aacs/ module refactors
Codec coverage (DVD + BD + UHD):
- E-AC-3 (Dolby Digital Plus): bsid detection, frame size calc — 8 tests
- DTS-HD MA/HR: extension substream (0x64582025) detection — 8 tests
- LPCM: BD header skip, raw PCM extraction — 6 tests
- DVD VobSub subtitles: passthrough parser — 5 tests
- Dolby Vision: verified RPU NAL type 62 preserved in HEVC — 1 test

Module refactors:
- disc.rs → disc/mod.rs + bluray.rs + dvd.rs + encrypt.rs
- aacs/mod.rs (1661 lines) → mod.rs (21) + keydb.rs + keys.rs + decrypt.rs
- All public APIs preserved, all tests pass

270 tests total, 0 failures.
2026-04-11 17:22:18 +00:00
MattJackson 187255106f Add crypto roundtrip tests: CSS + AACS validation
- CSS: decrypt_key determinism, descramble XOR roundtrip, TAB1 permutation,
  TAB4 bit-reversal involution, Stevenson attack on scrambled sector
- AACS: decrypt_unit roundtrip, disc hash deterministic, VUK derivation,
  unit key parsing, EC point-on-curve and ECDSA already covered
- 239 tests total
2026-04-11 17:17:29 +00:00
MattJackson fd4c88e969 CSS crypto tests + DVD pipeline fully wired
- CSS roundtrip tests: decrypt_key determinism, descramble XOR roundtrip
- CSS table verification: TAB1 is permutation, TAB4 is bit-reversal involution
- DVD scan pipeline confirmed: scan_dvd_titles, CSS crack, ContentReader descramble
- 229 tests, all passing
2026-04-11 17:14:58 +00:00
MattJackson 59e6916702 Rewrite CSS from Stevenson 1999 paper — proper table-driven cipher
- tables.rs: 5 CSS specification tables (TAB1-TAB5, mathematical constants)
- lfsr.rs: Table-driven LFSR1 (TAB2/TAB3) + LFSR0 (TAB4), sector seed XOR,
  decrypt_key() mangling function, descramble_sector() with proper feedback
- crack.rs: Stevenson divide-and-conquer attack (2^16 LFSR1 iteration,
  LFSR0 deduction from known plaintext, 10-byte validation)
- No external code copied — original Rust implementation from the 1999 paper
- 225 tests, 0 ignored
2026-04-11 17:07:21 +00:00
MattJackson 01235ff347 Audit fixes + DVD support foundation (IFO, PS demux, MPEG-2, CSS crack)
Audit fixes (14 critical, 22 warnings):
- UDF: bounds checks on all ICB/FID parsing from disc data
- SCSI Linux: saturating_sub on residual, CDB length guard, buffer size guard
- SCSI macOS: SCSITaskStatus u32 (was u8 — stack corruption)
- AACS: EC mod_inv returns infinity instead of panic, key reduced mod n
- AACS: do_handshake tries all host certs (was returning on first failure)
- H.264: bounds check on SPS < 4 bytes
- ContentReader: error on missing unit key (was zero-fill)
- KEYDB: flat redirect loop (was recursive), 100MB response limit, Windows HOME fallback
- ISO writer: AVDP extent order, partition length, allocation cap
- Network: removed TCP_NODELAY on bulk stream
- MKV: guard on u64::MAX seek
- disc.rs: saturating_sub on extent offset, simplified dead region code
- cargo fmt (610 violations), cargo clippy --fix (55 auto-fixes)

DVD support (new files):
- src/ifo.rs — IFO parser (VIDEO_TS.IFO, VTS_XX_0.IFO, PGC chains, cells, streams) — 13 tests
- src/mux/ps.rs — MPEG-2 Program Stream demuxer (pack headers, PES, private stream 1) — 12 tests
- src/mux/codec/mpeg2.rs — MPEG-2 video parser (sequence headers, I-frame detection) — 15 tests
- src/css/crack.rs — split-attack algorithm (LFSR cipher needs verification — test ignored)

226 tests total (was 186), 1 ignored (CSS crack needs cipher verification).
2026-04-11 16:52:22 +00:00
MattJackson 6de4ee4b5c v0.7.2: Windows SPTI, 177 tests, platform file separation 2026-04-11 16:15:14 +00:00
MattJackson 425583ea09 Refactor: drive discovery into platform files, no inline cfg
- drive/unix.rs: find_drives() + resolve_device() for Linux/macOS
- drive/windows.rs: find_drives() + resolve_device() + normalize_path() for Windows
- drive/mod.rs: clean delegation, no cfg branches
- scsi/windows.rs: SPTI transport only, no drive discovery
2026-04-11 16:12:53 +00:00
MattJackson f67a8aa8d5 Add Windows SPTI backend, CI check, platform support complete
- scsi/windows.rs: SCSI_PASS_THROUGH_DIRECT via DeviceIoControl
- Device path normalization (D:, \\.\CdRom0, \\.\D:)
- Windows drive discovery (CdRom0-15 + drive letters)
- CI: cargo check on windows-latest
- Platform table: Linux + macOS + Windows all supported
2026-04-11 16:08:30 +00:00
MattJackson 059bce6331 Add 113 tests, update CI to checkout@v5, add FEATURES.md
Test suite: 64 → 177 tests
- MPLS parser: 6 tests (synthetic binary, streams, errors)
- CLPI parser: 6 tests (EP map, PTS/SPN math, errors)
- H.264: 12 tests (NAL parsing, SPS/PPS, keyframes)
- HEVC: 13 tests (VPS/SPS/PPS, IRAP range, codec private)
- AC3: 12 tests (syncword, frame extraction)
- VC1: 15 tests (BITMAPINFOHEADER, start codes)
- DTS: 5, TrueHD: 4, PGS: 4 tests
- EBML: 6 tests (size/ID/string/float roundtrips)
- UDF: 10 tests (MockSectorReader, filesystem parsing, error paths)
- Disc: 8 tests (scan_image, DiscTitle helpers)
- Streams: 5 new (meta roundtrip, MkvStream)
- NullStream: 4, StdioStream: 2, IsoSectorReader: 2

CI: actions/checkout@v4 → v5 (all workflows)
FEATURES.md: created for v0.7.1
2026-04-11 16:02:49 +00:00
MattJackson cb099531c9 v0.7.1: SectorReader trait, IsoStream, StdioStream, resolve_encryption
- SectorReader trait decouples disc scanning from SCSI
- Disc::scan_image() for ISO and any sector source
- resolve_encryption() handles AACS 1.0/2.0/none in one path
- IsoStream: full UDF/MPLS/CLPI/labels pipeline from ISO files
- StdioStream: stdin/stdout pipe
- Strict scheme:// URL format with validation
- Labels module refactored to SectorReader
- 7 stream types total
2026-04-11 15:49:29 +00:00
MattJackson c79d3ab530 Fix update-readme: use ORG_DISPATCH_TOKEN to bypass branch protection 2026-04-11 15:04:22 +00:00
MattJackson 0cb6718238 Bump to v0.7.0 2026-04-11 14:55:52 +00:00
MattJackson 1d46ced137 Add StdioStream, IsoStream; enforce scheme:// URL format
- StdioStream: stdin/stdout pipe, format-agnostic
- IsoStream: read BD-TS from Blu-ray ISO images
- URL resolver: bare paths rejected, all URLs require scheme:// prefix
- Validation: empty paths, missing ports, read-only/write-only errors
- Tests: 22 passing (URL parsing, validation, metadata roundtrip)
- Docs: full stream table with 7 stream types
2026-04-11 14:54:28 +00:00
MattJackson bc9bf022ea Add IOStream trait and stream-based I/O architecture
Introduce IOStream trait for uniform read/write across disc, file,
network, and null streams. Rename Title→DiscTitle, add stream URL
resolver, split old stream.rs into focused modules (m2ts, mkvstream,
network, disc, null, resolve, meta).
2026-04-10 19:13:53 -07:00
MattJackson 026b6e7a43 Support multiple host certs, expose handshake_error on AacsState
- host_cert: Option → host_certs: Vec (try all until one succeeds)
- handshake_error field shows why auth failed (e.g. cert rejected/revoked)
2026-04-10 09:48:29 -07:00
MattJackson 914891feef Bump to v0.6.1 — republish with open() all drives, AACS 2.0, has_profile() 2026-04-10 09:30:15 -07:00
MattJackson b8b00f2121 v0.6.0: changelog, open() all drives, AACS 2.0, MKV muxer 2026-04-10 09:26:45 -07:00
MattJackson dd9f10439c Update README for v0.6.0: AACS 2.0 done, open() all drives, .cargo gitignore 2026-04-10 09:24:01 -07:00
MattJackson a07b2a85a4 Remove orphaned jar.rs — replaced by labels/ module 2026-04-10 09:17:11 -07:00
MattJackson c3e50f3a2e open() works on all drives, AACS 2.0 handshake wired, raw_gc_010c on DriveId
- DriveSession::open() no longer requires profile match — works on any optical drive
- init()/probe_disc() return error gracefully for unknown drives
- find_drives() returns all optical drives (PDT 0x05), not just profile-matched
- has_profile() check for callers
- AACS 2.0: handshake wired into resolve_aacs() — real VID + read_data_key
- DriveId: added raw_gc_010c field for GET_CONFIG 010C response bytes
2026-04-10 08:48:41 -07:00
MattJackson 5990ec9566 Fix test failures: correct EBML size assertion, fix doc tests
- Fix write_size test: 127 encodes as [0x40, 0x7F] not [0xC0, 0x7F]
- Mark doc examples as ignore (use incomplete pseudo-code)
2026-04-10 08:23:32 -07:00