Files
libfreemkv/docs/mkb.md
T
Matthew Jackson 6e14fc0c0c libfreemkv: 1.7.0 groundwork — sector-recycle soundness + CI hardening
Security
- Remove the unsafe Vec::set_len over recycled sector buffers — UB reachable
  from safe code (GHSA-j8ww-f5fg-9pmh, low). Uses Vec::resize; pooling unchanged.

CI / tooling
- clippy on stable with the MSRV single-sourced from Cargo.toml; cargo-deny
  dependency-audit gate with first-party git sources allowed (#15); audience-based
  comment-guard (#17).

Docs
- README (keydb ownership, BD-J parser count), drive-access transport/reset
  notes, mux timeline module-doc links.
2026-09-02 12:40:55 -07:00

70 lines
3.5 KiB
Markdown

# AACS MKB internals — relocated rationale
Supplementary notes for `src/aacs/mkb.rs` comments that exceed the
comment-guard's internal-comment cap. Each section is pointed to by a
short `// See docs/mkb.md — <topic>` comment at the relevant call site.
## Record-walker consolidation (`mkb_records`)
Yields `(offset, rec_type, rec_len)` for each record — a 4-byte header
(type byte + big-endian 24-bit length) then the body — stopping at the
`00 000000` end marker or a malformed/out-of-bounds length. It is lazy
(no body clone), so a find-one-record caller never materialises the
multi-MB cvalue table. `walk_mkb` and every MKB record walk in
`aacs::resolve`/`aacs::derive` are built on this, so the framing rules —
and any future fix to them — live in exactly one place. They had
previously drifted across six hand-rolled copies before being
consolidated here.
## cvalue record selection (`mkb_find_cvalues`)
The cvalue table is record type `0x05` (Media Key Data) on BOTH AACS 1.0
and AACS 2.x MKBs — its 16-byte cvalue entries are 1:1 with the 5-byte
Subset-Difference index entries in record `0x04` — the standard AACS MKB
layout.
On AACS 2.x in-drive UHD MKBs the `0x05` table is large (the full
subset-difference cvalue set: ~181k entries on a retail MKB, 1:1 with the
giant `0x04` index), while record `0x07` (Explicit Subset-Difference
Record) is a much smaller structure (~96 entries) and is NOT the cvalue
table. An earlier version of this function preferred `0x07`, which
under-tested the Subset-Difference walk on UHD discs and prevented the
DK→walk path from ever finding the matching uv. The selection MUST
therefore be `0x05`-first; `0x07` is only a fallback for
malformed/legacy MKBs that somehow lack a `0x05` record.
## BE24 length field test (`mkb_records_honors_the_high_byte_of_the_be24_length`)
The record length is a big-endian **24-bit** field, so the high byte
carries lengths of 64 KiB and up. The MKB records that matter most are
exactly that size — a real UHD cvalue table is `46_101 * 16` bytes and a
`0x2d` variant record is ~92 KiB — so a walker that dropped the high byte
would mis-frame every record of a real MKB from the first big one
onward, and every downstream key lookup would read the wrong bytes.
(The pre-existing high-byte test used total length `0x0110`, whose high
byte is ZERO — it exercised the middle byte only. This test puts a
non-zero value in the high byte.)
## Header-only record test (`mkb_records_yields_a_header_only_record_at_the_buffer_end`)
`rec_len == 4` is a well-formed HEADER-ONLY record (the minimum the
walker accepts), including one sitting at the very end of the buffer
with no bytes after it. Rejecting either — the `pos + 4` bound or the
`rec_len < 4` floor being off by one — silently drops the MKB's last
record, and "the record isn't there" is indistinguishable from "the disc
doesn't carry it".
## MKBType raw-bytes test (`mkb_type_raw_reads_all_four_body_bytes`)
`mkb_type_raw` reports the 32-bit MKBType field verbatim (`[C]`
§3.2.5.1.1 Table 3-2), including a value this build does not recognise —
the caller uses it to tell "unknown MKB generation" from "no Type record
at all". All four bytes must come from the record body; reading any of
them from the wrong offset yields a type that silently classifies as a
different AACS generation.
The recognised constants all share bytes with the `0x10` record-type
header byte (e.g. `MKB_21_CATEGORY_C` is `48 15 10 03`), so this test
uses a value with four distinct bytes, none of them `0x10`.