use crate::decrypt::{DecryptKeys, decrypt_sectors};
use crate::error::Result;
use super::SectorSource;
pub struct DecryptingSectorSource<S: SectorSource> {
inner: S,
keys: DecryptKeys,
unit_key_idx: usize,
}
impl<S: SectorSource> DecryptingSectorSource<S> {
pub fn new(inner: S, keys: DecryptKeys) -> Self {
Self {
inner,
keys,
unit_key_idx: 0,
}
}
pub fn with_unit_key_idx(mut self, idx: usize) -> Self {
self.unit_key_idx = idx;
self
}
pub fn set_keys(&mut self, keys: DecryptKeys) {
self.keys = keys;
}
pub fn inner(&self) -> &S {
&self.inner
}
pub fn inner_mut(&mut self) -> &mut S {
&mut self.inner
}
pub fn into_inner(self) -> S {
self.inner
}
}
impl<S: SectorSource> SectorSource for DecryptingSectorSource<S> {
fn capacity_sectors(&self) -> u32 {
self.inner.capacity_sectors()
}
fn read_sectors(
&mut self,
lba: u32,
count: u16,
buf: &mut [u8],
recovery: bool,
) -> Result<usize> {
let n = self.inner.read_sectors(lba, count, buf, recovery)?;
decrypt_sectors(&mut buf[..n], &self.keys, self.unit_key_idx)?;
Ok(n)
}
fn set_speed(&mut self, kbs: u16) {
self.inner.set_speed(kbs)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::Result;
struct PatternedSource {
capacity: u32,
}
impl PatternedSource {
fn fill(lba: u32, count: u16, buf: &mut [u8]) {
let bytes = count as usize * 2048;
for (i, slot) in buf[..bytes].iter_mut().enumerate() {
let abs = lba as u64 * 2048 + i as u64;
*slot = ((abs.wrapping_mul(2654435761) >> 16) & 0xff) as u8;
}
}
}
impl SectorSource for PatternedSource {
fn capacity_sectors(&self) -> u32 {
self.capacity
}
fn read_sectors(
&mut self,
lba: u32,
count: u16,
buf: &mut [u8],
_recovery: bool,
) -> Result<usize> {
Self::fill(lba, count, buf);
Ok(count as usize * 2048)
}
}
#[test]
fn passthrough_with_no_keys() {
let src = PatternedSource { capacity: 16 };
let mut wrapped = DecryptingSectorSource::new(src, DecryptKeys::None);
assert_eq!(wrapped.capacity_sectors(), 16);
let mut got = vec![0u8; 4 * 2048];
let n = wrapped.read_sectors(3, 4, &mut got, false).unwrap();
assert_eq!(n, 4 * 2048);
let mut expected = vec![0u8; 4 * 2048];
PatternedSource::fill(3, 4, &mut expected);
assert_eq!(got, expected);
}
#[test]
fn passthrough_set_speed_delegates() {
struct SpeedRecorder {
last: Option<u16>,
}
impl SectorSource for SpeedRecorder {
fn capacity_sectors(&self) -> u32 {
0
}
fn read_sectors(
&mut self,
_lba: u32,
_count: u16,
_buf: &mut [u8],
_recovery: bool,
) -> Result<usize> {
Ok(0)
}
fn set_speed(&mut self, kbs: u16) {
self.last = Some(kbs);
}
}
let mut wrapped =
DecryptingSectorSource::new(SpeedRecorder { last: None }, DecryptKeys::None);
wrapped.set_speed(7200);
assert_eq!(wrapped.inner().last, Some(7200));
}
use std::sync::{Arc, Mutex};
struct ShortReportSource {
report_n: usize,
}
impl ShortReportSource {
fn fill_one(buf: &mut [u8]) {
for (i, b) in buf.iter_mut().enumerate() {
*b = (i as u8).wrapping_mul(29).wrapping_add(3);
}
buf[0x14] = 0x30; }
}
impl SectorSource for ShortReportSource {
fn read_sectors(
&mut self,
_lba: u32,
count: u16,
buf: &mut [u8],
_recovery: bool,
) -> Result<usize> {
for s in 0..count as usize {
Self::fill_one(&mut buf[s * 2048..(s + 1) * 2048]);
}
Ok(self.report_n)
}
}
struct ArgRecorder {
calls: Arc<Mutex<Vec<(u32, u16, bool)>>>,
}
impl SectorSource for ArgRecorder {
fn read_sectors(
&mut self,
lba: u32,
count: u16,
buf: &mut [u8],
recovery: bool,
) -> Result<usize> {
self.calls.lock().unwrap().push((lba, count, recovery));
let bytes = count as usize * 2048;
buf[..bytes].fill(0);
Ok(bytes)
}
}
struct FailingSource;
impl SectorSource for FailingSource {
fn read_sectors(
&mut self,
_lba: u32,
_count: u16,
_buf: &mut [u8],
_recovery: bool,
) -> Result<usize> {
Err(crate::error::Error::IoError {
source: std::io::Error::from(std::io::ErrorKind::TimedOut),
})
}
}
#[test]
fn css_unscrambled_sector_passes_through() {
struct FixedSector {
template: [u8; 2048],
}
impl SectorSource for FixedSector {
fn read_sectors(
&mut self,
_lba: u32,
count: u16,
buf: &mut [u8],
_recovery: bool,
) -> Result<usize> {
let bytes = count as usize * 2048;
for s in 0..count as usize {
buf[s * 2048..(s + 1) * 2048].copy_from_slice(&self.template);
}
Ok(bytes)
}
}
let mut template = [0u8; 2048];
for (i, b) in template.iter_mut().enumerate() {
*b = (i as u8).wrapping_mul(13).wrapping_add(7);
}
template[0x14] = 0x00;
let expected = template;
let mut wrapped = DecryptingSectorSource::new(
FixedSector { template },
DecryptKeys::Css {
title_key: [0x11, 0x22, 0x33, 0x44, 0x55],
},
);
let mut got = [0u8; 2048];
let n = wrapped.read_sectors(0, 1, &mut got, false).unwrap();
assert_eq!(n, 2048);
assert_eq!(
got, expected,
"unscrambled CSS sector (flags=0) must pass through untouched"
);
}
#[test]
fn decrypt_span_bounded_by_reported_n() {
let mut wrapped = DecryptingSectorSource::new(
ShortReportSource { report_n: 0 },
DecryptKeys::Css {
title_key: [1, 2, 3, 4, 5],
},
);
let mut expected = vec![0u8; 2048];
ShortReportSource::fill_one(&mut expected);
let mut got = vec![0u8; 2048];
let n = wrapped.read_sectors(5, 1, &mut got, false).unwrap();
assert_eq!(n, 0, "decorator must return the inner source's n");
assert_eq!(
got, expected,
"with n=0 the decrypt span is empty; buffer must be untouched"
);
assert_eq!(got[0x14] & 0x30, 0x30, "scramble flags must remain set");
}
#[test]
fn args_forwarded_verbatim() {
let calls = Arc::new(Mutex::new(Vec::new()));
let mut wrapped = DecryptingSectorSource::new(
ArgRecorder {
calls: calls.clone(),
},
DecryptKeys::None,
);
let mut buf = vec![0u8; 2 * 2048];
wrapped.read_sectors(12345, 2, &mut buf, true).unwrap();
wrapped.read_sectors(0, 1, &mut buf, false).unwrap();
assert_eq!(
*calls.lock().unwrap(),
vec![(12345, 2, true), (0, 1, false)],
"lba/count/recovery must pass through unchanged"
);
}
#[test]
fn inner_read_error_propagates() {
let mut wrapped = DecryptingSectorSource::new(FailingSource, DecryptKeys::None);
let mut buf = vec![0u8; 2048];
let r = wrapped.read_sectors(0, 1, &mut buf, false);
let err = r.expect_err("inner error must propagate");
let io: std::io::Error = err.into();
assert_eq!(io.kind(), std::io::ErrorKind::TimedOut);
}
#[test]
fn aacs_missing_unit_key_errors() {
let src = PatternedSource { capacity: 16 };
let mut wrapped = DecryptingSectorSource::new(
src,
DecryptKeys::Aacs {
unit_keys: Vec::new(),
read_data_key: None,
},
);
let mut buf = vec![0u8; 2048];
let r = wrapped.read_sectors(0, 1, &mut buf, false);
let err = r.expect_err("missing unit key must error, not pass through encrypted");
assert_eq!(
err.code(),
crate::error::Error::DecryptFailed.code(),
"must surface DecryptFailed"
);
}
struct ClearUnitSource;
impl SectorSource for ClearUnitSource {
fn read_sectors(
&mut self,
_lba: u32,
count: u16,
buf: &mut [u8],
_recovery: bool,
) -> Result<usize> {
let bytes = count as usize * 2048;
buf[..bytes].fill(0);
let mut off = 4usize;
while off < bytes {
buf[off] = 0x47;
off += 192;
}
Ok(bytes)
}
}
#[test]
fn with_unit_key_idx_selects_key() {
let keys = DecryptKeys::Aacs {
unit_keys: vec![(0u32, [0u8; 16])],
read_data_key: None,
};
let mut buf = vec![0u8; 3 * 2048];
let mut bad =
DecryptingSectorSource::new(ClearUnitSource, keys.clone()).with_unit_key_idx(2);
assert!(
bad.read_sectors(0, 3, &mut buf, false).is_err(),
"out-of-range unit_key_idx must fail the lookup"
);
let mut good = DecryptingSectorSource::new(ClearUnitSource, keys).with_unit_key_idx(0);
let mut buf2 = vec![0u8; 3 * 2048];
let n = good.read_sectors(0, 3, &mut buf2, false).unwrap();
assert_eq!(n, 3 * 2048);
assert_eq!(
buf2[4], 0x47,
"clear unit must be left intact under valid idx"
);
}
#[test]
fn set_keys_swaps_active_keys() {
struct ScrambledSector {
template: [u8; 2048],
}
impl SectorSource for ScrambledSector {
fn read_sectors(
&mut self,
_lba: u32,
count: u16,
buf: &mut [u8],
_recovery: bool,
) -> Result<usize> {
let bytes = count as usize * 2048;
for s in 0..count as usize {
buf[s * 2048..(s + 1) * 2048].copy_from_slice(&self.template);
}
Ok(bytes)
}
}
let mut template = [0u8; 2048];
for (i, b) in template.iter_mut().enumerate() {
*b = (i as u8).wrapping_mul(29).wrapping_add(3);
}
template[0x14] = 0x30; let pristine = template;
let mut wrapped =
DecryptingSectorSource::new(ScrambledSector { template }, DecryptKeys::None);
let mut got = [0u8; 2048];
wrapped.read_sectors(0, 1, &mut got, false).unwrap();
assert_eq!(
got, pristine,
"None keys must pass the sector through unchanged"
);
assert_eq!(
got[0x14] & 0x30,
0x30,
"None must leave the scramble flags set"
);
wrapped.set_keys(DecryptKeys::Css {
title_key: [0xa1, 0xb2, 0xc3, 0xd4, 0xe5],
});
let mut got2 = [0u8; 2048];
wrapped.read_sectors(0, 1, &mut got2, false).unwrap();
assert_eq!(
got2[0x14] & 0x30,
0x00,
"CSS descramble must clear the scramble-control bits"
);
assert_ne!(
&got2[128..2048],
&pristine[128..2048],
"CSS descramble must alter the encrypted data region"
);
}
#[test]
fn inner_accessors_round_trip() {
let src = PatternedSource { capacity: 42 };
let mut wrapped = DecryptingSectorSource::new(src, DecryptKeys::None);
assert_eq!(wrapped.inner().capacity_sectors(), 42);
assert_eq!(wrapped.inner_mut().capacity_sectors(), 42);
let recovered = wrapped.into_inner();
assert_eq!(recovered.capacity_sectors(), 42);
}
}