diff --git a/src/aacs/boil.rs b/src/aacs/boil.rs index a9e868d..3511def 100644 --- a/src/aacs/boil.rs +++ b/src/aacs/boil.rs @@ -162,7 +162,7 @@ pub enum KeyCandidate { /// /// PURE DERIVATION — no unit sampling, no validation. `unit_keys` holds every /// CPS-unit key the disc's `Unit_Key_RO.inf` yields from the VUK (positional -/// order); the caller runs [`super::decrypt::unit_key_validates`] to find which +/// order); the caller runs [`super::content::unit_key_validates`] to find which /// one actually opens the disc. Rungs above the candidate are `None` (a `Vuk` /// candidate has no `mk`/`pk`/`dk`; a `Uk` candidate has only `unit_keys`). #[derive(Debug, Clone)] @@ -193,7 +193,7 @@ pub struct ResolvedChain { /// PURE DERIVATION: no sampling, no validation, no position recovery. Every step /// is deterministic AES, so the returned keys are only as sound as the input /// candidate — validate `unit_keys` against a real encrypted unit with -/// [`super::decrypt::unit_key_validates`] to prove the candidate opens the disc. +/// [`super::content::unit_key_validates`] to prove the candidate opens the disc. /// /// Returns `None` only when derivation itself cannot proceed: a PK its MKB /// rejects, a `Dk` the MKB can't process, a missing VID on a path that needs @@ -275,7 +275,7 @@ pub fn resolve_candidate( #[cfg(test)] mod tests { use super::*; - use crate::aacs::decrypt::aes_ecb_encrypt; + use crate::aacs::content::aes_ecb_encrypt; use crate::aacs::keys::{decrypt_unit_key, derive_vuk}; /// `vuk_from_mk` must equal the inline `derive_vuk` path bit-for-bit, for diff --git a/src/aacs/decrypt.rs b/src/aacs/content.rs similarity index 100% rename from src/aacs/decrypt.rs rename to src/aacs/content.rs diff --git a/src/aacs/keys.rs b/src/aacs/keys.rs index 6b34e81..43067f0 100644 --- a/src/aacs/keys.rs +++ b/src/aacs/keys.rs @@ -1,6 +1,6 @@ //! AACS key resolution — VUK derivation, MKB processing, disc hash, unit key parsing. -use super::decrypt::aes_ecb_decrypt; +use super::content::aes_ecb_decrypt; use super::types::DeviceKey; // ── AACS version ──────────────────────────────────────────────────────────── @@ -406,7 +406,7 @@ pub mod probe { fn mkb_find_mk_dv(mkb: &[u8]) -> Option<[u8; 16]> { // Verify-Media-Key record (0x81 for AACS 1.0, 0x86 for AACS 2.x): mk_dv is // the 16 bytes at record offset 4 (body offset 0). Needs rec_len >= 20. - let found = crate::aacs::variants::mkb_records(mkb) + let found = crate::aacs::variant::mkb_records(mkb) .find(|&(_, rt, len)| (rt == 0x81 || rt == 0x86) && len >= 20); match found { Some((o, rec_type, rec_len)) => { @@ -466,7 +466,7 @@ fn mkb_find_cvalues(mkb: &[u8]) -> Option> { /// record matching `rec_type`. Returns `None` if no such record exists or /// the record is empty. fn find_record_body(mkb: &[u8], rec_type_wanted: u8) -> Option> { - crate::aacs::variants::mkb_records(mkb) + crate::aacs::variant::mkb_records(mkb) .find(|&(_, rt, len)| rt == rec_type_wanted && len > 4) .map(|(o, _, len)| mkb[o + 4..o + len].to_vec()) } @@ -482,7 +482,7 @@ pub fn mkb_content_len(mkb: &[u8]) -> usize { // End of the last framed record = where the fixed-region zero padding begins. // (The `00 000000` terminator / overrun stops the walk; real MKBs pad with // zeros, so this matches the prior "stop at the first padding byte".) - crate::aacs::variants::mkb_records(mkb) + crate::aacs::variant::mkb_records(mkb) .last() .map(|(o, _, len)| o + len) .unwrap_or(0) @@ -511,7 +511,7 @@ pub fn trim_mkb(mut mkb: Vec) -> Vec { pub fn mkb_version(mkb: &[u8]) -> Option { // Type-and-Version record (0x10): version is the BE u32 at body offset 4 // (record offset 8). Needs rec_len >= 12 (4 header + 4 type + 4 version). - crate::aacs::variants::mkb_records(mkb) + crate::aacs::variant::mkb_records(mkb) .find(|&(_, rt, len)| rt == 0x10 && len >= 12) .map(|(o, _, _)| u32::from_be_bytes([mkb[o + 8], mkb[o + 9], mkb[o + 10], mkb[o + 11]])) } @@ -582,7 +582,7 @@ impl MkbType { pub fn mkb_type_raw(mkb: &[u8]) -> Option { // Type-and-Version record (0x10): the 32-bit MKBType is bytes 4-7 (body // offset 0). Needs rec_len >= 8 (4 header + 4 type). - crate::aacs::variants::mkb_records(mkb) + crate::aacs::variant::mkb_records(mkb) .find(|&(_, rt, len)| rt == 0x10 && len >= 8) .map(|(o, _, _)| u32::from_be_bytes([mkb[o + 4], mkb[o + 5], mkb[o + 6], mkb[o + 7]])) } @@ -1117,8 +1117,8 @@ pub fn resolve_keys_v1(ctx: &ResolveContext<'_>) -> Option { pub fn resolve_keys_v2(ctx: &ResolveContext<'_>) -> Option { let mut resolved = resolve_keys_classical(ctx, AacsVersion::V20)?; if let Some(mkb) = ctx.mkb { - let recs = super::variants::walk_mkb(mkb); - if super::variants::is_variant_mkb(&recs) { + let recs = super::variant::walk_mkb(mkb); + if super::variant::is_variant_mkb(&recs) { resolved.version = AacsVersion::V21; } } @@ -1131,7 +1131,7 @@ pub fn resolve_keys_v2(ctx: &ResolveContext<'_>) -> Option { /// 1. Variant chain: MKB Variant records + device keys → Km → Kvu /// (currently unreachable in production — requires an /// integrator-supplied Key Correction Data constant; see -/// [`super::variants::KEY_CORRECTION_DATA_PLACEHOLDER`]) +/// [`super::variant::KEY_CORRECTION_DATA_PLACEHOLDER`]) /// 3. KEYDB MK + matching VID → derived VUK (V21 discs already in /// the keydb decrypt identically to V20) /// 4. KEYDB disc-hash → VUK @@ -1181,12 +1181,12 @@ pub fn resolve_keys_v21(ctx: &ResolveContext<'_>) -> Option { // Path 1: Variant chain (V21's analogue of classical Path 1's // DK derivation). Placeholder until KCD constant is supplied. if let Some(mkb) = ctx.mkb { - let recs = super::variants::walk_mkb(mkb); + let recs = super::variant::walk_mkb(mkb); let all_dks = providers.device_keys(); - match super::variants::derive_media_key_variant( + match super::variant::derive_media_key_variant( &recs, &all_dks, - &super::variants::KEY_CORRECTION_DATA_PLACEHOLDER, + &super::variant::KEY_CORRECTION_DATA_PLACEHOLDER, ctx.volume_id, ) { Ok((_km, kvu)) => { @@ -1536,7 +1536,7 @@ mod tests { // keeps the crypto covered in libfreemkv. `aes_ecb_encrypt` is // pub(crate), reachable here but not from keysources — the reason this // half stays. - use super::super::decrypt::aes_ecb_encrypt; + use super::super::content::aes_ecb_encrypt; let vuk = [0x5Au8; 16]; // A few representative "decrypted" unit keys. for expected_uk in [[0x11u8; 16], [0x22u8; 16], [0xCDu8; 16]] { @@ -1726,7 +1726,7 @@ mod tests { // whose derived Media Key satisfies a synthetic verify record; confirm // the scan ACCEPTS it against caller-supplied SD/cvalue tables and // REJECTS a 1-byte corruption. - use super::super::decrypt::aes_ecb_encrypt as enc; + use super::super::content::aes_ecb_encrypt as enc; let pk: [u8; 16] = [ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, @@ -1772,7 +1772,7 @@ mod tests { // recovers mk. Catches the bugs that landed pre-fix: // * uv XOR step was missing → mk wrong whenever uv != 0 // * AES-128E + 12-zero check instead of AES-128D + magic - use super::super::decrypt::{aes_ecb_decrypt as dec, aes_ecb_encrypt as enc}; + use super::super::content::{aes_ecb_decrypt as dec, aes_ecb_encrypt as enc}; let pk: [u8; 16] = [ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, @@ -2191,7 +2191,7 @@ mod tests { // The keyless-disc case: this disc's own hash/VID are NOT in keydb, but its // Media Key IS — filed under a sibling disc that shares its MKB. Path // 2.5 must km_verifies that MK against the MKB and resolve. - use super::super::decrypt::aes_ecb_encrypt as enc; + use super::super::content::aes_ecb_encrypt as enc; let km = [0x11u8; 16]; let vid = [0x22u8; 16]; // MKB: 0x10 type/version + 0x86 verify record whose mk_dv decrypts under @@ -2272,7 +2272,7 @@ mod tests { // Independently compute AES-ECB-D(mk, vid) XOR vid and confirm // derive_vuk produces the same 16 bytes. A mutation that dropped the // XOR-VID step, or used encrypt instead of decrypt, fails this. - use super::super::decrypt::aes_ecb_decrypt as dec; + use super::super::content::aes_ecb_decrypt as dec; let mk = [ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, @@ -2292,7 +2292,7 @@ mod tests { // The encrypted unit key in Unit_Key_RO.inf is AES-ECB-E(VUK, uk); // decrypt_unit_key must be the matching ECB-decrypt. Round-trip via // encrypt to pin the relation. - use super::super::decrypt::aes_ecb_encrypt as enc; + use super::super::content::aes_ecb_encrypt as enc; let vuk = [0x9Eu8; 16]; let uk = [0x3Cu8; 16]; let enc_uk = enc(&vuk, &uk); @@ -2692,7 +2692,7 @@ mod tests { fn resolve_keys_v21_path4_resolves_by_hash() { // resolve_keys_v21 must hit path 4 (hash→VUK) and stamp version V21, // deriving unit keys from the VUK. - use super::super::decrypt::aes_ecb_encrypt as enc; + use super::super::content::aes_ecb_encrypt as enc; let data = build_unit_key_ro(1, 64); // The single encrypted key in build_unit_key_ro is [0x10;16]. let hash = disc_hash(&data); @@ -2842,7 +2842,7 @@ mod tests { // - 0x86 Verify Media Key: mk_dv = AES-E(mk, magic || pad) // and a DK with node=4, uv=2, u_mask_shift=3 so dev_key_v_mask == // v_mask: the calc_pk_from_dk loop is a no-op and Kp == aesg3(dk, 1). - use super::super::decrypt::aes_ecb_encrypt as enc; + use super::super::content::aes_ecb_encrypt as enc; let dk_bytes: [u8; 16] = [ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, diff --git a/src/aacs/mod.rs b/src/aacs/mod.rs index 556ecfb..4fe56d0 100644 --- a/src/aacs/mod.rs +++ b/src/aacs/mod.rs @@ -26,13 +26,13 @@ //! is silent (the `0x86` verify record and the Category-C MKBType names). pub mod boil; -pub mod decrypt; +pub mod content; pub mod host_certs; pub mod keys; pub mod provider; pub mod trace; pub mod types; -pub mod variants; +pub mod variant; /// On-disc UDF paths to the AACS key-input files (with their fallbacks). /// Centralised so every reader (`resolve_vid_only`, `read_aacs_inputs`, @@ -56,7 +56,7 @@ pub use trace::{KeyNode, KeyOutcome, KeyStep, ResolutionTrace, UnlockOutcome, Un // 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::{ +pub use content::{ ALIGNED_UNIT_LEN, ALIGNED_UNIT_SECTORS, UnitKeyResult, aacs_unit_encrypted, aacs_unit_needs_decrypt, aacs_unit_still_ciphertext, decrypt_bus, decrypt_unit, decrypt_unit_checked, decrypt_unit_full, decrypt_unit_try_keys, fill_null_ts_unit, @@ -79,7 +79,7 @@ pub use keys::{ }; pub use provider::KeyProvider; pub use types::{DeviceKey, DiscEntry, HostCert}; -pub use variants::{ +pub use variant::{ KEY_CORRECTION_DATA_PLACEHOLDER, MediaKeyVariantError, MkbRecord, ProcessingKeyMatch, derive_media_key_variant, is_variant_mkb, variant_nonce, walk_mkb, walk_processing_key, }; diff --git a/src/aacs/variants.rs b/src/aacs/variant.rs similarity index 99% rename from src/aacs/variants.rs rename to src/aacs/variant.rs index 802806e..a10b429 100644 --- a/src/aacs/variants.rs +++ b/src/aacs/variant.rs @@ -72,7 +72,7 @@ //! that final gate — the per-match magic check no longer protects the //! variant path. -use super::decrypt::aes_ecb_decrypt; +use super::content::aes_ecb_decrypt; use super::types::DeviceKey; // ── Public constants ────────────────────────────────────────────────────── @@ -743,7 +743,7 @@ mod tests { /// /// Returns (records, dk, planted_kp, planted_kmp). fn synthetic_variant_setup(kmp15: u8) -> (Vec, DeviceKey, [u8; 16], [u8; 16]) { - use crate::aacs::decrypt::aes_ecb_encrypt; + use crate::aacs::content::aes_ecb_encrypt; // Build header. let mut mkb = vec![ diff --git a/src/decrypt.rs b/src/decrypt.rs index 5dbdc21..1b88450 100644 --- a/src/decrypt.rs +++ b/src/decrypt.rs @@ -1121,7 +1121,7 @@ mod tests { /// Encrypt an aligned unit with the AACS algorithm run in reverse so that /// `aacs::decrypt_unit` with the same key recovers the plaintext. Mirrors - /// the `aacs_encrypt_unit` helper in `aacs::decrypt::tests`. + /// the `aacs_encrypt_unit` helper in `aacs::content::tests`. fn aacs_encrypt_unit_for_test(unit: &mut [u8], unit_key: &[u8; 16]) { use aes::Aes128; use aes::cipher::{BlockEncrypt, KeyInit, generic_array::GenericArray}; @@ -1129,13 +1129,13 @@ mod tests { // the per-unit key so the recovered plaintext header matches. unit[0] |= 0xC0; let header: [u8; 16] = unit[..16].try_into().unwrap(); - let derived = crate::aacs::decrypt::aes_ecb_encrypt(unit_key, &header); + let derived = crate::aacs::content::aes_ecb_encrypt(unit_key, &header); let mut k = [0u8; 16]; for i in 0..16 { k[i] = derived[i] ^ header[i]; } let cipher = Aes128::new(GenericArray::from_slice(&k)); - let mut prev = crate::aacs::decrypt::AACS_IV; + let mut prev = crate::aacs::content::AACS_IV; let num_blocks = (aacs::ALIGNED_UNIT_LEN - 16) / 16; for i in 0..num_blocks { let off = 16 + i * 16; diff --git a/src/disc/extract.rs b/src/disc/extract.rs index 1e842d5..4b718a5 100644 --- a/src/disc/extract.rs +++ b/src/disc/extract.rs @@ -1101,13 +1101,13 @@ mod tests { // Flag encrypted via CPI bits (byte 0) before key derivation. unit[0] |= 0xC0; let header: [u8; 16] = unit[..16].try_into().unwrap(); - let derived = crate::aacs::decrypt::aes_ecb_encrypt(unit_key, &header); + let derived = crate::aacs::content::aes_ecb_encrypt(unit_key, &header); let mut k = [0u8; 16]; for i in 0..16 { k[i] = derived[i] ^ header[i]; } let cipher = Aes128::new(GenericArray::from_slice(&k)); - let mut prev = crate::aacs::decrypt::AACS_IV; + let mut prev = crate::aacs::content::AACS_IV; let blocks = (crate::aacs::ALIGNED_UNIT_LEN - 16) / 16; for i in 0..blocks { let o = 16 + i * 16; diff --git a/src/disc/mod.rs b/src/disc/mod.rs index a2880e8..6423256 100644 --- a/src/disc/mod.rs +++ b/src/disc/mod.rs @@ -2201,7 +2201,7 @@ fn aligned_unit_keys_validate( read_data_key: Option<&[u8; 16]>, samples: &[Vec], ) -> bool { - use crate::aacs::decrypt::{ALIGNED_UNIT_LEN, aacs_unit_needs_decrypt, decrypt_unit_full}; + use crate::aacs::content::{ALIGNED_UNIT_LEN, aacs_unit_needs_decrypt, decrypt_unit_full}; let scrambled: Vec<&[u8]> = samples .iter() .map(|s| s.as_slice()) @@ -5116,7 +5116,7 @@ mod tests { #[test] fn unit_key_validation_gates_on_real_ciphertext() { - use crate::aacs::decrypt::{ALIGNED_UNIT_LEN, ts_sync_destroyed}; + use crate::aacs::content::{ALIGNED_UNIT_LEN, ts_sync_destroyed}; // No samples -> nothing to disprove against -> accept (sample-less paths // like resume / mapfile must be unaffected). @@ -5174,7 +5174,7 @@ mod tests { // CPS-unit-1 sectors then passed through as raw encrypted bytes into the // ISO/MKV with no error surfaced. The gate must now reject a key set // that leaves any scrambled sample uncovered. - use crate::aacs::decrypt::{ALIGNED_UNIT_LEN, ts_sync_destroyed}; + use crate::aacs::content::{ALIGNED_UNIT_LEN, ts_sync_destroyed}; let mut clear = vec![0u8; ALIGNED_UNIT_LEN]; let mut off = 4; @@ -5220,7 +5220,7 @@ mod tests { /// unit algorithm — ECB-derive the per-unit key, then AES-CBC encrypt the /// body with the fixed AACS IV. fn encrypt_unit_for_test(clear: &[u8], uk: &[u8; 16]) -> Vec { - use crate::aacs::decrypt::{AACS_IV, ALIGNED_UNIT_LEN}; + use crate::aacs::content::{AACS_IV, ALIGNED_UNIT_LEN}; use aes::Aes128; use aes::cipher::{BlockEncrypt, KeyInit, generic_array::GenericArray}; let mut unit = clear[..ALIGNED_UNIT_LEN].to_vec(); diff --git a/src/disc/verify.rs b/src/disc/verify.rs index 492e662..3c8c33c 100644 --- a/src/disc/verify.rs +++ b/src/disc/verify.rs @@ -523,13 +523,13 @@ mod tests { use aes::cipher::{BlockEncrypt, KeyInit, generic_array::GenericArray}; unit[0] |= 0xC0; // CPI flag => reads as encrypted let header: [u8; 16] = unit[..16].try_into().unwrap(); - let derived = crate::aacs::decrypt::aes_ecb_encrypt(unit_key, &header); + let derived = crate::aacs::content::aes_ecb_encrypt(unit_key, &header); let mut k = [0u8; 16]; for i in 0..16 { k[i] = derived[i] ^ header[i]; } let cipher = Aes128::new(GenericArray::from_slice(&k)); - let mut prev = crate::aacs::decrypt::AACS_IV; + let mut prev = crate::aacs::content::AACS_IV; for i in 0..(ALIGNED_UNIT_LEN - 16) / 16 { let off = 16 + i * 16; for j in 0..16 { diff --git a/src/sector/decrypting.rs b/src/sector/decrypting.rs index 115bac7..d394abc 100644 --- a/src/sector/decrypting.rs +++ b/src/sector/decrypting.rs @@ -1267,13 +1267,13 @@ mod tests { // CPI bits on byte 0 so it reads as encrypted; set before key derivation. unit[0] |= 0xC0; let header: [u8; 16] = unit[..16].try_into().unwrap(); - let derived = crate::aacs::decrypt::aes_ecb_encrypt(unit_key, &header); + let derived = crate::aacs::content::aes_ecb_encrypt(unit_key, &header); let mut k = [0u8; 16]; for i in 0..16 { k[i] = derived[i] ^ header[i]; } let cipher = Aes128::new(GenericArray::from_slice(&k)); - let mut prev = crate::aacs::decrypt::AACS_IV; + let mut prev = crate::aacs::content::AACS_IV; let blocks = (crate::aacs::ALIGNED_UNIT_LEN - 16) / 16; for i in 0..blocks { let o = 16 + i * 16;