use aes::Aes128;
use aes::cipher::{BlockDecrypt, BlockEncrypt, KeyInit, generic_array::GenericArray};
pub(crate) const AACS_IV: [u8; 16] = [
0x0B, 0xA0, 0xF8, 0xDD, 0xFE, 0xA6, 0x1F, 0xB3, 0xD8, 0xDF, 0x9F, 0x56, 0x6A, 0x05, 0x0F, 0x78,
];
pub const ALIGNED_UNIT_LEN: usize = 6144;
const SECTOR_LEN: usize = 2048;
const TS_PACKET_LEN: usize = 192;
const TS_SYNC: u8 = 0x47;
pub(crate) fn aes_ecb_encrypt(key: &[u8; 16], data: &[u8; 16]) -> [u8; 16] {
let cipher = Aes128::new(GenericArray::from_slice(key));
let mut block = GenericArray::clone_from_slice(data);
cipher.encrypt_block(&mut block);
let mut out = [0u8; 16];
out.copy_from_slice(&block);
out
}
pub(crate) fn aes_ecb_decrypt(key: &[u8; 16], data: &[u8; 16]) -> [u8; 16] {
let cipher = Aes128::new(GenericArray::from_slice(key));
let mut block = GenericArray::clone_from_slice(data);
cipher.decrypt_block(&mut block);
let mut out = [0u8; 16];
out.copy_from_slice(&block);
out
}
pub(crate) fn aes_cbc_decrypt(key: &[u8; 16], data: &mut [u8]) {
let cipher = Aes128::new(GenericArray::from_slice(key));
let num_blocks = data.len() / 16;
for i in (0..num_blocks).rev() {
let offset = i * 16;
let prev = if i == 0 {
AACS_IV
} else {
let mut p = [0u8; 16];
p.copy_from_slice(&data[(i - 1) * 16..i * 16]);
p
};
let mut block = GenericArray::clone_from_slice(&data[offset..offset + 16]);
cipher.decrypt_block(&mut block);
for j in 0..16 {
data[offset + j] = block[j] ^ prev[j];
}
}
}
pub fn is_aacs_scrambled(unit: &[u8]) -> bool {
unit.len() >= ALIGNED_UNIT_LEN && !ts_syncs_intact(unit)
}
pub fn ts_sync_count(unit: &[u8]) -> usize {
let mut count = 0;
let mut offset = 4;
while offset < unit.len() {
if unit[offset] == TS_SYNC {
count += 1;
}
offset += TS_PACKET_LEN;
}
count
}
pub fn ts_packet_total(unit: &[u8]) -> usize {
unit.len() / TS_PACKET_LEN
}
fn ts_syncs_intact(unit: &[u8]) -> bool {
ts_sync_count(unit) > ts_packet_total(unit) / 2
}
fn verify_ts(unit: &[u8]) -> bool {
ts_syncs_intact(unit)
}
pub fn decrypt_unit(unit: &mut [u8], unit_key: &[u8; 16]) -> bool {
if unit.len() < ALIGNED_UNIT_LEN {
return false;
}
if !is_aacs_scrambled(unit) {
return true; }
let mut header = [0u8; 16];
header.copy_from_slice(&unit[..16]);
let derived = aes_ecb_encrypt(unit_key, &header);
let mut decrypt_key = [0u8; 16];
for i in 0..16 {
decrypt_key[i] = derived[i] ^ header[i];
}
aes_cbc_decrypt(&decrypt_key, &mut unit[16..ALIGNED_UNIT_LEN]);
verify_ts(unit)
}
pub fn unit_key_validates(unit: &[u8], unit_key: &[u8; 16]) -> bool {
if unit.len() < ALIGNED_UNIT_LEN {
return false;
}
let mut header = [0u8; 16];
header.copy_from_slice(&unit[..16]);
let derived = aes_ecb_encrypt(unit_key, &header);
let mut decrypt_key = [0u8; 16];
for i in 0..16 {
decrypt_key[i] = derived[i] ^ header[i];
}
const SYNC_PAYLOAD_OFF: usize = 196;
let region_off = SYNC_PAYLOAD_OFF - 16; let blk = region_off / 16; let byte = region_off % 16; let c11 = 16 + blk * 16; let cipher = Aes128::new(GenericArray::from_slice(&decrypt_key));
let mut b = GenericArray::clone_from_slice(&unit[c11..c11 + 16]);
cipher.decrypt_block(&mut b);
let prev = unit[c11 - 16 + byte]; if b[byte] ^ prev != TS_SYNC {
return false;
}
let mut full = [0u8; ALIGNED_UNIT_LEN];
full.copy_from_slice(&unit[..ALIGNED_UNIT_LEN]);
decrypt_unit(&mut full, unit_key)
}
pub fn decrypt_unit_try_keys(unit: &mut [u8], unit_keys: &[[u8; 16]]) -> Option<usize> {
if !is_aacs_scrambled(unit) {
return Some(0);
}
let original = unit[..ALIGNED_UNIT_LEN].to_vec();
for (i, key) in unit_keys.iter().enumerate() {
unit[..ALIGNED_UNIT_LEN].copy_from_slice(&original);
if decrypt_unit(unit, key) {
return Some(i);
}
}
unit[..ALIGNED_UNIT_LEN].copy_from_slice(&original);
None
}
pub fn decrypt_bus(unit: &mut [u8], read_data_key: &[u8; 16]) {
for sector_start in (0..ALIGNED_UNIT_LEN).step_by(SECTOR_LEN) {
if sector_start + SECTOR_LEN > unit.len() {
break;
}
aes_cbc_decrypt(
read_data_key,
&mut unit[sector_start + 16..sector_start + SECTOR_LEN],
);
}
}
pub fn decrypt_unit_full(
unit: &mut [u8],
unit_key: &[u8; 16],
read_data_key: Option<&[u8; 16]>,
) -> bool {
if !is_aacs_scrambled(unit) {
return true;
}
if let Some(rdk) = read_data_key {
decrypt_bus(unit, rdk);
}
decrypt_unit(unit, unit_key)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_aes_ecb_roundtrip() {
let key = [
0x15u8, 0x66, 0x5F, 0x98, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
0x0B, 0x0C,
];
let plain = [0x41u8; 16];
let enc = aes_ecb_encrypt(&key, &plain);
let dec = aes_ecb_decrypt(&key, &enc);
assert_eq!(dec, plain);
}
#[test]
fn test_decrypt_unit_unencrypted() {
let mut unit = vec![0u8; ALIGNED_UNIT_LEN];
let mut off = 4;
while off < ALIGNED_UNIT_LEN {
unit[off] = TS_SYNC;
off += TS_PACKET_LEN;
}
let key = [0u8; 16];
assert!(!is_aacs_scrambled(&unit));
assert!(decrypt_unit(&mut unit, &key));
}
#[test]
fn test_aes_cbc_roundtrip() {
let key = [
0x11u8, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE,
0xFF, 0x00,
];
let original = vec![0x42u8; 128]; let mut data = original.clone();
fn aes_cbc_encrypt(key: &[u8; 16], data: &mut [u8]) {
let cipher = Aes128::new(GenericArray::from_slice(key));
let mut prev = super::AACS_IV;
let num_blocks = data.len() / 16;
for i in 0..num_blocks {
let offset = i * 16;
for j in 0..16 {
data[offset + j] ^= prev[j];
}
let mut block = GenericArray::clone_from_slice(&data[offset..offset + 16]);
cipher.encrypt_block(&mut block);
data[offset..offset + 16].copy_from_slice(&block);
prev.copy_from_slice(&data[offset..offset + 16]);
}
}
aes_cbc_encrypt(&key, &mut data);
assert_ne!(data, original);
super::aes_cbc_decrypt(&key, &mut data);
assert_eq!(data, original); }
#[test]
fn test_decrypt_unit_synthetic() {
let unit_key = [0xAAu8; 16];
let mut plain = vec![0u8; ALIGNED_UNIT_LEN];
let mut offset = 4;
while offset < ALIGNED_UNIT_LEN {
plain[offset] = TS_SYNC;
offset += TS_PACKET_LEN;
}
let header: [u8; 16] = plain[..16].try_into().unwrap();
let derived = aes_ecb_encrypt(&unit_key, &header);
let mut encrypt_key = [0u8; 16];
for i in 0..16 {
encrypt_key[i] = derived[i] ^ header[i];
}
let cipher = Aes128::new(GenericArray::from_slice(&encrypt_key));
let mut prev = AACS_IV;
let num_blocks = (ALIGNED_UNIT_LEN - 16) / 16;
for i in 0..num_blocks {
let off = 16 + i * 16;
for j in 0..16 {
plain[off + j] ^= prev[j];
}
let mut block = GenericArray::clone_from_slice(&plain[off..off + 16]);
cipher.encrypt_block(&mut block);
plain[off..off + 16].copy_from_slice(&block);
prev.copy_from_slice(&plain[off..off + 16]);
}
let mut unit = plain;
assert!(is_aacs_scrambled(&unit));
assert!(decrypt_unit(&mut unit, &unit_key));
assert!(!is_aacs_scrambled(&unit));
let mut count = 0;
let mut off = 4;
while off < ALIGNED_UNIT_LEN {
if unit[off] == TS_SYNC {
count += 1;
}
off += TS_PACKET_LEN;
}
assert_eq!(count, (ALIGNED_UNIT_LEN - 4) / TS_PACKET_LEN + 1);
}
}