diff --git a/.gitignore b/.gitignore index ea8c4bf..ab23b67 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ /target +__pycache__/ +.vscode \ No newline at end of file diff --git a/src/formats/vestel/mod.rs b/src/formats/vestel/mod.rs index 1a3f6b3..5c66730 100644 --- a/src/formats/vestel/mod.rs +++ b/src/formats/vestel/mod.rs @@ -2,76 +2,107 @@ mod include; use std::any::Any; use crate::AppContext; -use std::fs::{self, OpenOptions}; -use std::io::Write; - +use std::fs; use crate::utils::common; +use crate::utils::aes::decrypt_aes128_ecb; use crate::keys; -use crate::utils::aes::{decrypt_aes128_ecb}; + use include::*; -pub fn is_vestel_file(app_ctx: &AppContext) -> Result>, Box> { - let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)}; +pub fn is_vestel_file( + app_ctx: &AppContext, +) -> Result>, Box> { + let mut file = match app_ctx.file() { + Some(f) => f, + None => return Ok(None), + }; + let file_size = file.metadata()?.len() as usize; - let header = common::read_file(&file, 0, 32)?; - if header.windows(6).any(|w| w == b"VESTEL") { - return Ok(Some(Box::new(VestelCtx { is_encrypted: false }))); + // 1. FAST PATH: plaintext check + let header = common::read_file(&file, 0, 64)?; + + if header.windows(5).any(|w| w == b"OPTEE") { + return Ok(Some(Box::new(VestelCtx { + is_encrypted: false, + }))); } - let vestel_key = keys::VESTEL.first(); - if vestel_key.is_none() { - return Ok(None); - } - let (_key_name, key_hex) = vestel_key.unwrap(); + // 2. KEY CHECK + let (_, key_hex) = match keys::VESTEL.first() { + Some(k) => k, + None => return Ok(None), + }; - let key = hex::decode(key_hex)?; - if key.len() != 16 { - return Ok(None); - } - let key_bytes: [u8; 16] = key.try_into().map_err(|_| "Invalid key length")?; - - if file_size % 16 != 0 { + let key_vec = hex::decode(key_hex)?; + if key_vec.len() != 16 { return Ok(None); } - let data = common::read_exact(&mut file.try_clone()?, file_size)?; - let decrypted = decrypt_aes128_ecb(&key_bytes, &data)?; + let key: [u8; 16] = match key_vec.try_into() { + Ok(k) => k, + Err(_) => return Ok(None), + }; - if decrypted.windows(6).any(|w| w == b"VESTEL") { - Ok(Some(Box::new(VestelCtx { is_encrypted: true }))) - } else { - Ok(None) + // 3. SIZE CHECK + if file_size < 32 { + return Ok(None); } + + // 4. Read file + let data = common::read_exact(&mut file, file_size)?; + + // 5. Python equivalent trimming + let cut_len = data.len().saturating_sub(16) & !0xF; + + if cut_len == 0 { + return Ok(None); + } + + // 6. decrypt candidate + let decrypted = decrypt_aes128_ecb(&key, &data[..cut_len])?; + + // 7. verify OPTEE in decrypted output + if decrypted.windows(5).any(|w| w == b"OPTEE") { + return Ok(Some(Box::new(VestelCtx { + is_encrypted: true, + }))); + } + + Ok(None) } -pub fn extract_vestel(app_ctx: &AppContext, ctx: Box) -> Result<(), Box> { +pub fn extract_vestel( + 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 file_size = file.metadata()?.len() as usize; let data = common::read_exact(&mut file, file_size)?; - let (key_name, key_hex) = keys::VESTEL.first() + let (_, key_hex) = keys::VESTEL + .first() .ok_or("VESTEL key not found!")?; - let key = hex::decode(key_hex)?; - let key_len = key.len(); - let key_bytes = match key.try_into() { - Ok(bytes) => bytes, - Err(_) => return Err(format!("Invalid VESTEL key length: expected 16 bytes, got {}", key_len).into()), - }; + let key_vec = hex::decode(key_hex)?; + let key: [u8; 16] = key_vec.try_into().map_err(|_| "Invalid AES key length")?; - let (decrypted_data, decrypted_path) = if ctx.is_encrypted { - println!("Detected encrypted Vestel firmware, using key: {} ({})", key_name, key_hex); - println!("Decrypting firmware..."); - let dec_data = decrypt_aes128_ecb(&key_bytes, &data)?; + // decrypt if needed + let (final_data, decrypted_path) = if ctx.is_encrypted { + println!("Encrypted Vestel firmware detected"); + println!("Using key: {} ({})", "VESTEL", key_hex); + + let dec = decrypt_aes128_ecb(&key, &data)?; + + let output_path = std::path::Path::new(&app_ctx.output_dir) + .join("_decrypted.bin"); - let output_path = std::path::Path::new(&app_ctx.output_dir).join("_decrypted.bin"); fs::create_dir_all(&app_ctx.output_dir)?; - let mut out_file = OpenOptions::new().write(true).create(true).truncate(true).open(&output_path)?; - out_file.write_all(&dec_data)?; - (dec_data, Some(output_path)) + fs::write(&output_path, &dec)?; + + (dec, Some(output_path)) } else { println!("Detected Vestel firmware (unencrypted)"); (data, None) @@ -80,30 +111,39 @@ pub fn extract_vestel(app_ctx: &AppContext, ctx: Box) -> Result<(), Box let partitions = VESTEL_PARTITIONS; println!("\nPartition count: {}", partitions.len()); + fs::create_dir_all(&app_ctx.output_dir)?; + for (name, (offset, size)) in partitions.iter() { - let end = offset + size; - if end > decrypted_data.len() { - println!("\nWarning: Partition {} extends beyond firmware bounds (offset=0x{:X}, size=0x{:X})", name, offset, size); + let start = *offset; + let end = start.saturating_add(*size); + + if start >= final_data.len() { + println!("Skipping {} (out of range)", name); + continue; } - let segment = &decrypted_data[*offset..std::cmp::min(end, decrypted_data.len())]; + let end = end.min(final_data.len()); + let segment = &final_data[start..end]; - println!("\n{} - Offset: 0x{:X}, Size: 0x{:X}", name, offset, size); + println!( + "\n{} - Offset: 0x{:X}, Size: 0x{:X}", + name, offset, size + ); - let output_path = std::path::Path::new(&app_ctx.output_dir).join(format!("{}.bin", name)); + let output_path = std::path::Path::new(&app_ctx.output_dir) + .join(format!("{}.bin", name)); - fs::create_dir_all(&app_ctx.output_dir)?; - let mut out_file = OpenOptions::new().write(true).create(true).truncate(true).open(output_path)?; - out_file.write_all(segment)?; + fs::write(&output_path, segment)?; println!("- Saved {}.bin", name); } + // cleanup decrypted temp file if not needed if let Some(path) = decrypted_path { if !app_ctx.has_option("vestel:keep_decrypted") { - fs::remove_file(&path)?; + let _ = fs::remove_file(path); } } Ok(()) -} +} \ No newline at end of file diff --git a/src/keys.rs b/src/keys.rs index 8ab716f..bae24db 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -172,4 +172,4 @@ pub static EPK: &[(&str, &str)] = &[ //Vestel keys pub static VESTEL: &[(&str, &str)] = &[ ("VESTEL-MB180/MB130/MB181", "12345678123456781234567812345678"), -]; +]; \ No newline at end of file