libfreemkv: clip-anchored AACS unit gate + consolidate key mechanism
The AACS unit-alignment gate measured `lba % 3` against absolute disc LBA 0, but aligned units are anchored at each clip's encrypted-region start. A clip whose start_lba is not 3-aligned had its readable units wrongly rejected with "Decryption failed" (the big-title-only failure on some Blu-rays). One canonical clip-anchored helper (`aacs::is_unit_aligned`) is now the single source of truth for the decrypt-on-read gate; both mux read paths set the per-extent `unit_base = start_lba` via a new `SectorSource::set_unit_base`. Also moves key *mechanism* into the library: the encrypted sample reader (`read_encrypted_units`) and the candidate-key resolution loop (`resolve_and_apply`) now live here, so a key source is purely a lookup. Regression test covers a clip based at a non-3-aligned LBA.
This commit is contained in:
@@ -13,6 +13,26 @@ pub(crate) const AACS_IV: [u8; 16] = [
|
||||
/// Size of an AACS aligned unit (3 × 2048-byte sectors).
|
||||
pub const ALIGNED_UNIT_LEN: usize = 6144;
|
||||
|
||||
/// An AACS aligned unit spans this many 2048-byte sectors (3).
|
||||
pub const ALIGNED_UNIT_SECTORS: u32 = (ALIGNED_UNIT_LEN / SECTOR_LEN) as u32;
|
||||
|
||||
/// Whether `lba` sits on an AACS aligned-unit boundary, measured **relative to
|
||||
/// the encrypted region's base LBA** (`unit_base` = the clip/extent `start_lba`,
|
||||
/// NOT absolute disc LBA 0).
|
||||
///
|
||||
/// AACS aligned units (6144 B / 3 sectors) are anchored at the start of each
|
||||
/// clip's encrypted region, so a read must begin a whole number of units past
|
||||
/// that base for `decrypt_sectors` (which anchors units at buffer offset 0) to
|
||||
/// align the CBC correctly. This is the SINGLE source of truth for the test —
|
||||
/// the decrypt-on-read gate, the inline and highway mux read paths, and the
|
||||
/// key-validation sample reader all key off this, never absolute `lba % 3`. A
|
||||
/// disc whose clip `start_lba` is not itself 3-aligned would otherwise mis-gate
|
||||
/// (reject readable units, then report "Decryption failed") on exactly the
|
||||
/// titles whose clips land off a 3-boundary.
|
||||
pub fn is_unit_aligned(lba: u32, unit_base: u32) -> bool {
|
||||
lba.wrapping_sub(unit_base) % ALIGNED_UNIT_SECTORS == 0
|
||||
}
|
||||
|
||||
/// Size of one sector.
|
||||
const SECTOR_LEN: usize = 2048;
|
||||
|
||||
|
||||
+3
-2
@@ -24,8 +24,9 @@ pub mod variants;
|
||||
// Explicit re-exports — only items needed by external consumers and sibling crate modules.
|
||||
// AES primitives (aes_ecb_encrypt, aes_ecb_decrypt, aes_cbc_decrypt) are pub(crate) in decrypt.rs.
|
||||
pub use decrypt::{
|
||||
ALIGNED_UNIT_LEN, UnitKeyResult, decrypt_bus, decrypt_unit, decrypt_unit_full,
|
||||
decrypt_unit_try_keys, is_aacs_scrambled, ts_packet_total, ts_sync_count, unit_key_validates,
|
||||
ALIGNED_UNIT_LEN, ALIGNED_UNIT_SECTORS, UnitKeyResult, decrypt_bus, decrypt_unit,
|
||||
decrypt_unit_full, decrypt_unit_try_keys, is_aacs_scrambled, is_unit_aligned, ts_packet_total,
|
||||
ts_sync_count, unit_key_validates,
|
||||
};
|
||||
pub use keydb::{DeviceKey, DiscEntry, HostCert, KeyDb};
|
||||
pub use keys::probe;
|
||||
|
||||
Reference in New Issue
Block a user