diff --git a/Cargo.lock b/Cargo.lock index 3519e31..c3a66da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -903,9 +903,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.18.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" [[package]] name = "unicode-ident" diff --git a/README.md b/README.md index 7620acb..1fd3379 100644 --- a/README.md +++ b/README.md @@ -221,7 +221,7 @@ Tip: if you have split ROM (.ROM-00 and .ROM-01), extract both into the same fol ## Sony BDP **Used in:** Sony MediaTek-based Blu-Ray players -**Notes:** Only platforms up to MSB18 are supported. +**Notes:** **Depends on keys** - see keys.rs (Platforms up to MSB29 are supported) **Thanks to:** http://malcolmstagg.com/bdp/s390-firmware.html # License diff --git a/src/formats/sony_bdp/include.rs b/src/formats/sony_bdp/include.rs index 1859f1d..176b522 100644 --- a/src/formats/sony_bdp/include.rs +++ b/src/formats/sony_bdp/include.rs @@ -1,7 +1,12 @@ use crate::utils::common; use binrw::BinRead; -//thx sony +pub enum EncryptionType { + HexSubst, + AesOfb(([u8; 16], [u8; 16], String)), //key, iv, key name +} + +//for hex subst (old enc) static HEX_SUBSTITUTION: [u8; 256] = [ 0xE8, 0x4D, 0x63, 0xF4, 0xF8, 0xA9, 0x21, 0x9C, 0xC7, 0x82, 0xCD, 0xE3, 0xC1, 0xCE, 0xC0, 0xFA, 0xE7, 0xD6, 0x96, 0x46, 0x12, 0x03, 0x14, 0x33, 0xED, 0x10, 0xEC, 0x69, 0x16, 0xE0, 0x28, 0x30, 0x77, 0x0E, 0x3D, 0xEF, 0x36, 0x4C, 0x18, 0xEB, 0x41, 0x89, 0x64, 0x8A, 0x70, 0x0C, 0x23, 0xA3, 0x79, 0x6D, 0x75, 0x7E, 0x1A, 0x2D, 0x01, 0x91, 0x88, 0xCB, 0xFC, 0x8B, 0xFD, 0x94, 0x0A, 0x39, @@ -17,6 +22,31 @@ pub fn hex_substitute(data: &[u8]) -> Vec { data.iter().map(|&b| HEX_SUBSTITUTION[b as usize]).collect() } + +//for aes (new enc) +use aes::Aes128; +use aes::cipher::{BlockEncrypt, KeyInit}; +use aes::cipher::generic_array::GenericArray; + +pub fn ver_up_decrypt_aes128ofb(key: &[u8], iv: &[u8], data: &[u8]) -> Vec { + let cipher = Aes128::new(GenericArray::from_slice(key)); + + let mut output = Vec::with_capacity(data.len()); + let mut feedback = GenericArray::clone_from_slice(iv); + for chunk in data.chunks(16) { + cipher.encrypt_block(&mut feedback); + for (b, k) in chunk.iter().zip(feedback.iter()) { + output.push(b ^ k); + } + } + output +} + +pub fn is_valid_header_magic(data: &[u8]) -> bool { + matches!(data, [b'M', b'S', b'B', d1, d2, ..] if d1.is_ascii_digit() && d2.is_ascii_digit()) || + matches!(data, [b'B', b'D', b'P', b'P', d1, d2, ..] if d1.is_ascii_digit() && d2.is_ascii_digit()) +} + #[derive(BinRead)] pub struct Header { firmware_name_bytes: [u8; 8], diff --git a/src/formats/sony_bdp/mod.rs b/src/formats/sony_bdp/mod.rs index 51524cb..9f0b459 100644 --- a/src/formats/sony_bdp/mod.rs +++ b/src/formats/sony_bdp/mod.rs @@ -5,29 +5,70 @@ use crate::{InputTarget, AppContext}; use std::fs::File; use std::path::{Path, PathBuf}; use std::fs::{self, OpenOptions}; -use std::io::{Cursor, Write}; +use std::io::{Cursor, Read, Seek, SeekFrom, Write}; use binrw::BinReaderExt; use crate::utils::common; use crate::formats; +use crate::keys; use include::*; +struct SonyBdpCtx { + encryption_type: EncryptionType, +} + pub fn is_sony_bdp_file(app_ctx: &AppContext) -> Result>, Box> { let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; + let header_magic = common::read_file(&file, 0, 16)?; - let header = common::read_file(&file, 0, 4)?; - if header == b"\x01\x73\xEC\xC9" || header == b"\x01\x73\xEC\x1F" || header == b"\xEC\x7D\xB0\xB0" { //MSB1x, MSB0x, BDPPxx - Ok(Some(Box::new(()))) - } else { - Ok(None) + //try old encryption (hex subst) + if is_valid_header_magic(&hex_substitute(&header_magic)) { + return Ok(Some(Box::new( + SonyBdpCtx {encryption_type: + EncryptionType::HexSubst + } + ))); } + + //try new encryption (aes) + for (key_hex, iv_hex, name) in keys::SONY_BDP_AES { + let key_array: [u8; 16] = hex::decode(key_hex)?.as_slice().try_into()?; + let iv_array: [u8; 16] = hex::decode(iv_hex)?.as_slice().try_into()?; + let try_decrypt = ver_up_decrypt_aes128ofb(&key_array, &iv_array, &header_magic); + + if is_valid_header_magic(&try_decrypt) { + return Ok(Some(Box::new( + SonyBdpCtx {encryption_type: + EncryptionType::AesOfb((key_array, iv_array, name.to_string())) + } + ))); + } + } + + Ok(None) } -pub fn extract_sony_bdp(app_ctx: &AppContext, _ctx: Box) -> Result<(), Box> { +pub fn extract_sony_bdp(app_ctx: &AppContext, ctx: Box) -> Result<(), Box> { let mut file = app_ctx.file().ok_or("Extractor expected file")?; + let ctx = ctx.downcast::().expect("Missing context"); - let obf_header = common::read_exact(&mut file, 300)?; - let header = hex_substitute(&obf_header); + //need to decrypt entire file of new aes enc + let mut enc_data = Vec::new(); + file.read_to_end(&mut enc_data)?; + + let dec_data = match ctx.encryption_type { + EncryptionType::HexSubst => { + println!("Decrypting with hex substitution..."); + hex_substitute(&enc_data) + }, + EncryptionType::AesOfb((key, iv, key_name)) => { + println!("Decrypting with AES key: {}...", key_name); + ver_up_decrypt_aes128ofb(&key, &iv, &enc_data) + } + }; + let mut data_reader = Cursor::new(dec_data); + + let header = common::read_exact(&mut data_reader, 300)?; let mut hdr_reader = Cursor::new(header); let hdr: Header = hdr_reader.read_le()?; @@ -53,8 +94,8 @@ pub fn extract_sony_bdp(app_ctx: &AppContext, _ctx: Box) -> Result<(), first_entry_offset = entry.offset as u64; } - let obf_data = common::read_file(&file, entry.offset as u64, entry.size as usize)?; - let data = hex_substitute(&obf_data); + data_reader.seek(SeekFrom::Start(entry.offset as u64))?; + let data = common::read_exact(&mut data_reader, entry.size as usize)?; let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", i+1)); last_file_path = Some(output_path.clone()); @@ -67,19 +108,19 @@ pub fn extract_sony_bdp(app_ctx: &AppContext, _ctx: Box) -> Result<(), i += 1; } - //The last file is often a Mtk BDP file so we can extract that here. + //The last file is the host MTK BDP file so we can extract that here (wont work for pre-linux which have old mtk bdp though.) if last_file_path.is_some() { - println!("\nChecking if it's also MTK BDP..."); - let last_file = File::open(last_file_path.unwrap())?; let mtk_extraction_path = app_ctx.output_dir.join(format!("{}", i)); - //this is getting stupid... - let ctx: AppContext = AppContext { input: InputTarget::File(last_file), output_dir: mtk_extraction_path, options: app_ctx.options.clone() }; + let ctx: AppContext = AppContext { + input: InputTarget::File(last_file), + output_dir: mtk_extraction_path, + options: app_ctx.options.clone() + }; if let Some(result) = formats::mtk_bdp::is_mtk_bdp_file(&ctx)? { println!("- MTK BDP file detected!\n"); - formats::mtk_bdp::extract_mtk_bdp(&ctx, result)?; } else { println!("- Not an MTK BDP file."); diff --git a/src/keys.rs b/src/keys.rs index d3830b6..c15433e 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -131,6 +131,13 @@ pub static FUNAI_BDP: &[&str] = &[ ("09E87783"), ]; +//Sony BDP AES keys +//key, iv(der), desc +pub static SONY_BDP_AES: &[(&str, &str, &str)] = &[ + ("2EC0855737CADA76E1D82F9D5526B46E", "8F5E39A2A36AD9B0A9D2E3041AA03867", "MSB19-23"), + ("0D8B87A8CE27702197155B8AC057E13A", "C280F2F534B1E53421D31AC4BE56CA43", "MSB24-29"), +]; + //EPK keys //github.com/openlgtv/epk2extract pub static EPK: &[(&str, &str)] = &[