mirror of
https://github.com/Ap0dexMe0/unixtract.git
synced 2026-07-16 05:04:22 +02:00
msd10,11: implement OUITH parsers for Tizen, some key cleanup, debug print in old parser
This commit is contained in:
+27
-39
@@ -1,12 +1,13 @@
|
||||
use std::fs::{self, File, OpenOptions};
|
||||
use std::path::{Path};
|
||||
use std::io::{Cursor, Write, Seek, SeekFrom};
|
||||
use std::io::{Write, Seek, SeekFrom};
|
||||
use binrw::{BinRead, BinReaderExt};
|
||||
|
||||
use crate::utils::common;
|
||||
use crate::keys;
|
||||
use crate::formats::msd::{decrypt_aes_salted_old, decrypt_aes_salted_tizen, decrypt_aes_tizen};
|
||||
use crate::utils::msd_ouith_parser_old::{parse_ouith_blob};
|
||||
use crate::utils::msd_ouith_parser_tizen_1_8::{parse_blob_1_8};
|
||||
|
||||
#[derive(BinRead)]
|
||||
struct FileHeader {
|
||||
@@ -40,21 +41,6 @@ struct Section {
|
||||
size: u32,
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
struct TizenTocEntry {
|
||||
#[br(count = 44)] _unk1: Vec<u8>,
|
||||
_name_length: u8,
|
||||
#[br(count = _name_length)] name_bytes: Vec<u8>,
|
||||
#[br(count = 314)] _unk2: Vec<u8>,
|
||||
#[br(count = 8)] salt: Vec<u8>,
|
||||
#[br(count = 13)] _unk3: Vec<u8>,
|
||||
}
|
||||
impl TizenTocEntry {
|
||||
fn name(&self) -> String {
|
||||
common::string_from_bytes(&self.name_bytes)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_msd10_file(file: &File) -> bool {
|
||||
let header = common::read_file(&file, 0, 6).expect("Failed to read from file.");
|
||||
if header == b"MSDU10" {
|
||||
@@ -118,31 +104,37 @@ pub fn extract_msd10(mut file: &File, output_folder: &str) -> Result<(), Box<dyn
|
||||
//parse TOC
|
||||
if firmware_type == "tizen" {
|
||||
let toc = decrypt_aes_salted_tizen(&toc_data, &passphrase_bytes)?;
|
||||
let mut toc_reader = Cursor::new(toc);
|
||||
let (items, info) = parse_blob_1_8(&toc)?;
|
||||
|
||||
toc_reader.seek(SeekFrom::Current(256))?; // probably signature
|
||||
toc_reader.seek(SeekFrom::Current(50))?; // Tizen Software Upgrade Tree Binary Format ver. 1.8
|
||||
if let Some(info) = info {
|
||||
println!("\nImage info:\n{} {}.{} {}/{}/{}",
|
||||
info.name(), info.major_ver, info.minor_ver, info.date_day, info.date_month, info.date_year);
|
||||
}
|
||||
|
||||
for i in 0..header.section_count {
|
||||
let entry: TizenTocEntry = toc_reader.read_le()?;
|
||||
let offset = sections[i as usize].offset;
|
||||
for (i, item) in items.iter().enumerate() {
|
||||
let size = sections[i as usize].size;
|
||||
let offset = sections[i as usize].offset;
|
||||
|
||||
println!("\n({}/{}) - {}, Size: {}", sections[i as usize].index, sections.len(), entry.name(), size);
|
||||
println!("\n({}/{}) - {}, Size: {}",
|
||||
item.item_id, items.len(), item.name, size);
|
||||
|
||||
let encrypted_data = common::read_file(&file, offset as u64, size as usize)?;
|
||||
assert!(sections[i as usize].index == item.item_id, "Item ID in TOC does not match ID from header!");
|
||||
|
||||
println!("- Decrypting...");
|
||||
let decrypted_data = decrypt_aes_tizen(&encrypted_data, &passphrase_bytes, &entry.salt)?;
|
||||
let stored_data = common::read_file(&file, offset as u64, size as usize)?;
|
||||
|
||||
let output_path = Path::new(&output_folder).join(entry.name());
|
||||
let out_data;
|
||||
if item.aes_encryption {
|
||||
println!("- Decrypting...");
|
||||
let salt = item.aes_salt.as_ref().ok_or("AES salt missing!")?;
|
||||
out_data = decrypt_aes_tizen(&stored_data, &passphrase_bytes, salt)?;
|
||||
} else {
|
||||
out_data = stored_data;
|
||||
}
|
||||
|
||||
let output_path = Path::new(&output_folder).join(item.name.clone());
|
||||
fs::create_dir_all(&output_folder)?;
|
||||
let mut out_file = OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(output_path)?;
|
||||
|
||||
out_file.write_all(&decrypted_data)?;
|
||||
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
|
||||
out_file.write_all(&out_data)?;
|
||||
|
||||
println!("-- Saved file!");
|
||||
|
||||
@@ -158,6 +150,7 @@ pub fn extract_msd10(mut file: &File, output_folder: &str) -> Result<(), Box<dyn
|
||||
}
|
||||
|
||||
for (i, item) in items.iter().enumerate() {
|
||||
let offset = sections[i as usize].offset;
|
||||
let type_str = if item.item_type == 0x0A {"Partition"} else if item.item_type == 0x0B {"File"} else if item.item_type == 0x11 {"CMAC Data"} else {"Unknown"};
|
||||
println!("\n({}/{}) - {}, Type: {}, Size: {}",
|
||||
item.item_id, items.len(), item.name, type_str, item.all_size);
|
||||
@@ -169,7 +162,6 @@ pub fn extract_msd10(mut file: &File, output_folder: &str) -> Result<(), Box<dyn
|
||||
continue
|
||||
}
|
||||
|
||||
let offset = sections[i as usize].offset;
|
||||
file.seek(SeekFrom::Start(offset as u64))?;
|
||||
|
||||
//skip heading metadata thing
|
||||
@@ -186,11 +178,7 @@ pub fn extract_msd10(mut file: &File, output_folder: &str) -> Result<(), Box<dyn
|
||||
|
||||
let output_path = Path::new(&output_folder).join(item.name.clone());
|
||||
fs::create_dir_all(&output_folder)?;
|
||||
let mut out_file = OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(output_path)?;
|
||||
|
||||
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
|
||||
out_file.write_all(&out_data)?;
|
||||
|
||||
println!("-- Saved file!");
|
||||
|
||||
+24
-33
@@ -1,11 +1,12 @@
|
||||
use std::fs::{self, File, OpenOptions};
|
||||
use std::path::{Path};
|
||||
use std::io::{Cursor, Write, Seek, SeekFrom};
|
||||
use std::io::{Write};
|
||||
use binrw::{BinRead, BinReaderExt};
|
||||
|
||||
use crate::utils::common;
|
||||
use crate::keys;
|
||||
use crate::formats::msd::{decrypt_aes_salted_tizen, decrypt_aes_tizen};
|
||||
use crate::utils::msd_ouith_parser_tizen_1_9::{parse_blob_1_9};
|
||||
|
||||
#[derive(BinRead)]
|
||||
struct FileHeader {
|
||||
@@ -40,21 +41,6 @@ struct Section {
|
||||
size: u64,
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
struct TocEntry {
|
||||
#[br(count = 74)] _unk1: Vec<u8>,
|
||||
_name_length: u8,
|
||||
#[br(count = _name_length)] name_bytes: Vec<u8>,
|
||||
#[br(count = 39)] _unk2: Vec<u8>,
|
||||
#[br(count = 8)] salt: Vec<u8>,
|
||||
#[br(count = 267)] _unk3: Vec<u8>,
|
||||
}
|
||||
impl TocEntry {
|
||||
fn name(&self) -> String {
|
||||
common::string_from_bytes(&self.name_bytes)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_msd11_file(file: &File) -> bool {
|
||||
let header = common::read_file(&file, 0, 6).expect("Failed to read from file.");
|
||||
if header == b"MSDU11" {
|
||||
@@ -112,32 +98,37 @@ pub fn extract_msd11(mut file: &File, output_folder: &str) -> Result<(), Box<dyn
|
||||
let toc_data = common::read_file(&file, toc_offset as u64, toc_size as usize)?;
|
||||
|
||||
let toc = decrypt_aes_salted_tizen(&toc_data, &passphrase_bytes)?;
|
||||
let mut toc_reader = Cursor::new(toc);
|
||||
let (items, info) = parse_blob_1_9(&toc)?;
|
||||
|
||||
toc_reader.seek(SeekFrom::Current(262))?; // probably signature
|
||||
toc_reader.seek(SeekFrom::Current(50))?; // Tizen Software Upgrade Tree Binary Format ver. 1.9
|
||||
if let Some(info) = info {
|
||||
println!("\nImage info:\n{} {}.{}",
|
||||
info.name(), info.major_ver, info.minor_ver);
|
||||
}
|
||||
|
||||
for i in 0..header.section_count {
|
||||
let entry: TocEntry = toc_reader.read_le()?;
|
||||
let offset = sections[i as usize].offset;
|
||||
for (i, item) in items.iter().enumerate() {
|
||||
let size = sections[i as usize].size;
|
||||
let offset = sections[i as usize].offset;
|
||||
|
||||
println!("\n({}/{}) - {}, Size: {}", sections[i as usize].index, sections.len(), entry.name(), size);
|
||||
println!("\n({}/{}) - {}, Size: {}",
|
||||
item.item_id, items.len(), item.name, size);
|
||||
|
||||
let encrypted_data = common::read_file(&file, offset as u64, size as usize)?;
|
||||
assert!(sections[i as usize].index == item.item_id, "Item ID in TOC does not match ID from header!");
|
||||
|
||||
println!("- Decrypting...");
|
||||
let decrypted_data = decrypt_aes_tizen(&encrypted_data, &passphrase_bytes, &entry.salt)?;
|
||||
let stored_data = common::read_file(&file, offset as u64, size as usize)?;
|
||||
|
||||
let output_path = Path::new(&output_folder).join(entry.name());
|
||||
let out_data;
|
||||
if item.aes_encryption {
|
||||
println!("- Decrypting...");
|
||||
let salt = item.aes_salt.as_ref().ok_or("AES salt missing!")?;
|
||||
out_data = decrypt_aes_tizen(&stored_data, &passphrase_bytes, salt)?;
|
||||
} else {
|
||||
out_data = stored_data;
|
||||
}
|
||||
|
||||
let output_path = Path::new(&output_folder).join(item.name.clone());
|
||||
fs::create_dir_all(&output_folder)?;
|
||||
let mut out_file = OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(output_path)?;
|
||||
|
||||
out_file.write_all(&decrypted_data)?;
|
||||
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
|
||||
out_file.write_all(&out_data)?;
|
||||
|
||||
println!("-- Saved file!");
|
||||
}
|
||||
|
||||
+10
-10
@@ -25,21 +25,21 @@ pub static MSD10: &[(&str, &str, &str)] = &[
|
||||
("T-N14MJ", "old", "95d01e0bae861a05695bc8a6edb2ea835a09accd"), //NT14M_J - 2014 Novatek (low-end) (for 2015 models)
|
||||
|
||||
//github.com/bugficks/msddecrypt
|
||||
("T-HKM", "tizen", "1ac8989ff57db5e75ea67b033050871c"),
|
||||
("T-HKP", "tizen", "cce8a3ef92f3e94895999e928f4dd6c3"),
|
||||
("T-HKM", "tizen", "1ac8989ff57db5e75ea67b033050871c"), //HawkM - 2015 Samsung
|
||||
("T-HKP", "tizen", "cce8a3ef92f3e94895999e928f4dd6c3"), //HawkP - 2015 Samsung
|
||||
];
|
||||
|
||||
//MSD11 keys
|
||||
//github.com/bugficks/msddecrypt
|
||||
pub static MSD11: &[(&str, &str)] = &[
|
||||
("T-JZM", "9b1d077c0d137d406c79ddacb6b159fe"), //2016
|
||||
("T-HKM", "c7097975e8ab994beb5eaae57e0ba77c"), //2016
|
||||
("T-KTM2L", "46b04f5e794ca4377a20951c9ea00427"), //2018
|
||||
("T-KTM2", "29110e0ce940b3a9b67d3e158f3f1342"), //2018
|
||||
("T-KTM", "d0d49d5f36f5c0da50062fbf32168f5b"), //2017
|
||||
("T-KTSU", "19e1ba41163f03735e692d9daa2cbb47"), //2018
|
||||
("T-KTSD", "39332605ff47a0aea999b10ce9087389"), //2018
|
||||
("T-NKL", "5bab1098dab48792619ebd63650d929f"), //2020
|
||||
("T-HKM", "c7097975e8ab994beb5eaae57e0ba77c"), //HawkM - 2015 Samsung (for 2016 models)
|
||||
("T-JZM", "9b1d077c0d137d406c79ddacb6b159fe"), //JazzM - 2016 Samsung
|
||||
("T-KTM2L", "46b04f5e794ca4377a20951c9ea00427"), //KantM2L - 2018 Samsung
|
||||
("T-KTM2", "29110e0ce940b3a9b67d3e158f3f1342"), //KantM2 - 2018 Samsung
|
||||
("T-KTM", "d0d49d5f36f5c0da50062fbf32168f5b"), //KantM - 2017 Samsung
|
||||
("T-KTSU", "19e1ba41163f03735e692d9daa2cbb47"), //KantSU - 2018 Samsung
|
||||
("T-KTSD", "39332605ff47a0aea999b10ce9087389"), //KantSD? - ???? Samsung
|
||||
("T-NKL", "5bab1098dab48792619ebd63650d929f"), //NikeL - 2020 Samsung
|
||||
];
|
||||
|
||||
//RUF keys
|
||||
|
||||
+3
-1
@@ -9,4 +9,6 @@ pub mod android_ota_update_metadata;
|
||||
pub mod mtk_crypto;
|
||||
pub mod lzhs;
|
||||
pub mod huffman_tables;
|
||||
pub mod msd_ouith_parser_old;
|
||||
pub mod msd_ouith_parser_old;
|
||||
pub mod msd_ouith_parser_tizen_1_8;
|
||||
pub mod msd_ouith_parser_tizen_1_9;
|
||||
@@ -5,6 +5,9 @@ use binrw::{BinRead, BinReaderExt};
|
||||
|
||||
use crate::utils::common;
|
||||
|
||||
// whether to print the tree
|
||||
static CONFIG_PRINT_TREE: bool = false;
|
||||
|
||||
#[derive(BinRead)]
|
||||
struct ChunkHeader {
|
||||
size: u32,
|
||||
@@ -20,9 +23,9 @@ struct DescriptorHeader {
|
||||
#[derive(BinRead)]
|
||||
//OUSWFileVersionDesc, OUPartitionVersionDesc, OUCMACDataDesc
|
||||
struct CommonDestinationInfo {
|
||||
_name_len: u8,
|
||||
#[br(count = _name_len)] name_bytes: Vec<u8>,
|
||||
_version: u16,
|
||||
name_len: u8,
|
||||
#[br(count = name_len)] name_bytes: Vec<u8>,
|
||||
version: u16,
|
||||
}
|
||||
impl CommonDestinationInfo {
|
||||
fn name(&self) -> String {
|
||||
@@ -32,36 +35,36 @@ impl CommonDestinationInfo {
|
||||
|
||||
#[derive(BinRead)]
|
||||
struct OUAESEncryptionDesc {
|
||||
_mode: u8,
|
||||
_key_size: u32,
|
||||
_salt_size: u32,
|
||||
mode: u8,
|
||||
key_size: u32,
|
||||
salt_size: u32,
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
struct OURSAValidationDesc {
|
||||
_mode: u8,
|
||||
mode: u8,
|
||||
_unknown: u32,
|
||||
_signature_size: u32,
|
||||
signature_size: u32,
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
struct OUSecureHashValidationDesc {
|
||||
_mode: u8,
|
||||
_hash_size: u16,
|
||||
#[br(count = _hash_size)] hash: Vec<u8>,
|
||||
mode: u8,
|
||||
hash_size: u16,
|
||||
#[br(count = hash_size)] hash: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
struct OUGroupDesc {
|
||||
_group_id: u32,
|
||||
_field_2: u8,
|
||||
_field_3: u8,
|
||||
group_id: u32,
|
||||
field_2: u8,
|
||||
field_3: u8,
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
pub struct OUSWImageVersionExDesc {
|
||||
pub _name_len: u8,
|
||||
#[br(count = _name_len)] pub name_bytes: Vec<u8>,
|
||||
pub name_len: u8,
|
||||
#[br(count = name_len)] pub name_bytes: Vec<u8>,
|
||||
pub major_ver: u16,
|
||||
pub minor_ver: u16,
|
||||
pub date_year: u8,
|
||||
@@ -96,37 +99,52 @@ pub fn parse_ouith_blob(blob: &[u8]) -> Result<(Vec<MSDItem>, Option<OUSWImageVe
|
||||
|
||||
let _signature = common::read_exact(&mut reader, 128)?; //signature included at the beginning of blob in MSD file
|
||||
|
||||
let mut _chunk_n = 0;
|
||||
let mut chunk_n = 0;
|
||||
while reader.stream_position()? < blob.len() as u64 {
|
||||
_chunk_n += 1;
|
||||
chunk_n += 1;
|
||||
let chunk: ChunkHeader = reader.read_be()?;
|
||||
if CONFIG_PRINT_TREE { println!("\nChunk {} - Size: {}, Value: {}", chunk_n, chunk.size, chunk.value); };
|
||||
let chunk_end = reader.stream_position()? + chunk.size as u64;
|
||||
|
||||
//parse top level descriptor. it can only be ID 1(OUUpgradeItemDesc) or 2(OUGroupDesc)
|
||||
let top_descriptor: DescriptorHeader = reader.read_be()?;
|
||||
if top_descriptor.tag == 0x01 {
|
||||
if CONFIG_PRINT_TREE { println!("OUUpgradeItemDesc(0x01) - Size: {}", top_descriptor.size); };
|
||||
|
||||
let item_id: u32 = reader.read_be()?;
|
||||
if CONFIG_PRINT_TREE { println!(" Item ID: {}", item_id); };
|
||||
|
||||
//REQUIRED are items in order: OUDestinationDesc(0x03), OUDataProcessingDesc(0x07), OUGroupInfoDesc(0x13). OPTIONAL items: OUDependenciesDesc(0x04), OUDataPostProcessingDesc(0x08)
|
||||
//In MSD files, no others seem to be used than the required ones. We will ignore all data after required descriptors.
|
||||
let destination_descriptor: DescriptorHeader = reader.read_be()?;
|
||||
if destination_descriptor.tag != 0x03 {return Err(format!("Unexpected descriptor type in OUUpgradeItemDesc, Expected: 0x03, Got: 0x{:02x}!", destination_descriptor.tag).into())}
|
||||
let _out_size: u32 = reader.read_be()?;
|
||||
if CONFIG_PRINT_TREE { println!(" OUDestinationDesc(0x03) - Size: {}", destination_descriptor.size); };
|
||||
let out_size: u32 = reader.read_be()?;
|
||||
if CONFIG_PRINT_TREE { println!(" Out data size: {}", out_size); };
|
||||
|
||||
//OUDestinationDesc needs one of OUSWFileVersionDesc(0x0B), OUPartitionVersionDesc(0x0A), OUCMACDataDesc(0x11). Their structure is the same. so we can store the type
|
||||
let type_descriptor: DescriptorHeader = reader.read_be()?;
|
||||
if ![0x0B, 0xA, 0x11].contains(&type_descriptor.tag) {return Err(format!("Unexpected descriptor type in OUDestinationDesc, Expected: one of 0x0B, 0x0A, 0x11, Got: 0x{:02x}!", type_descriptor.tag).into())}
|
||||
if CONFIG_PRINT_TREE { println!(" Type descriptor(0x{:02x}) - Size: {}", type_descriptor.tag, type_descriptor.size); };
|
||||
let destination_info: CommonDestinationInfo = reader.read_be()?;
|
||||
if CONFIG_PRINT_TREE {
|
||||
println!(" Name lenght: {}", destination_info.name_len);
|
||||
println!(" Name: {}", destination_info.name());
|
||||
println!(" Version: {}", destination_info.version);
|
||||
};
|
||||
|
||||
//OUDataProcessingDesc can have OUXOREncryptionDesc(0x0D), OUAESEncryptionDesc(0x0E), OUCompressionDesc(0x0F), OUSecureHashValidationDesc(0x18), OURSAValidationDesc(0x10), OUDataCopyDesc(0x16), OUKeepCurrentDataDesc(0x1E), OUCRC32ValidationDesc(0x12)
|
||||
let data_processing_descriptor: DescriptorHeader = reader.read_be()?;
|
||||
if data_processing_descriptor.tag != 0x07 {return Err(format!("Unexpected descriptor type in OUUpgradeItemDesc, Expected: 0x07, Got: 0x{:02x}!", data_processing_descriptor.tag).into())}
|
||||
if CONFIG_PRINT_TREE { println!(" OUDataProcessingDesc(0x07) - Size: {}", data_processing_descriptor.size); };
|
||||
let heading_size: u32 = reader.read_be()?;
|
||||
if CONFIG_PRINT_TREE { println!(" Heading size: {}", heading_size); };
|
||||
let data_size: u32 = reader.read_be()?;
|
||||
if CONFIG_PRINT_TREE { println!(" Data size: {}", data_size); };
|
||||
|
||||
let mut aes_encryption = false;
|
||||
let mut _crc32_hash: Option<u32> = None;
|
||||
let mut _secure_hash: Option<Vec<u8>> = None;
|
||||
//let mut crc32_hash: Option<u32> = None;
|
||||
//let mut secure_hash: Option<Vec<u8>> = None;
|
||||
|
||||
let epos = reader.stream_position()? + (data_processing_descriptor.size - 8) as u64;
|
||||
while reader.stream_position()? < epos {
|
||||
@@ -134,25 +152,46 @@ pub fn parse_ouith_blob(blob: &[u8]) -> Result<(Vec<MSDItem>, Option<OUSWImageVe
|
||||
if ![0x0D, 0x0E, 0x0F, 0x18, 0x10, 0x16, 0x1E, 0x12].contains(&descriptor.tag) {return Err(format!("Unexpected descriptor type in OUDataProcessingDesc, Expected: one of 0x0D, 0x0E, 0x0F, 0x18, 0x10, 0x16, 0x1E, 0x12, Got: 0x{:02x}!", descriptor.tag).into())}
|
||||
if descriptor.tag == 0x0E {
|
||||
//OUAESEncryptionDesc
|
||||
let _aes_encryption_desc: OUAESEncryptionDesc = reader.read_be()?;
|
||||
if CONFIG_PRINT_TREE { println!(" OUAESEncryptionDesc(0x0E) - Size: {}", descriptor.size); };
|
||||
let aes_encryption_desc: OUAESEncryptionDesc = reader.read_be()?;
|
||||
if CONFIG_PRINT_TREE {
|
||||
println!(" Mode: {}", aes_encryption_desc.mode);
|
||||
println!(" Key size: {}", aes_encryption_desc.key_size);
|
||||
println!(" Salt size: {}", aes_encryption_desc.salt_size);
|
||||
};
|
||||
aes_encryption = true;
|
||||
}
|
||||
else if descriptor.tag == 0x10 {
|
||||
//OURSAValidationDesc
|
||||
let _rsa_validation_desc: OURSAValidationDesc = reader.read_be()?;
|
||||
if CONFIG_PRINT_TREE { println!(" OURSAValidationDesc(0x10) - Size: {}", descriptor.size); };
|
||||
let rsa_validation_desc: OURSAValidationDesc = reader.read_be()?;
|
||||
if CONFIG_PRINT_TREE {
|
||||
println!(" Mode: {}", rsa_validation_desc.mode);
|
||||
println!(" Field 2: {}", rsa_validation_desc._unknown);
|
||||
println!(" Signature size: {}", rsa_validation_desc.signature_size);
|
||||
};
|
||||
}
|
||||
else if descriptor.tag == 0x12 {
|
||||
//OUCRC32ValidationDesc
|
||||
if CONFIG_PRINT_TREE { println!(" OUCRC32ValidationDesc(0x12) - Size: {}", descriptor.size); };
|
||||
let crc32: u32 = reader.read_be()?;
|
||||
_crc32_hash = Some(crc32);
|
||||
if CONFIG_PRINT_TREE { println!(" CRC32: {:02x}", crc32); };
|
||||
//crc32_hash = Some(crc32);
|
||||
}
|
||||
else if descriptor.tag == 0x18 {
|
||||
//OUSecureHashValidationDesc
|
||||
if CONFIG_PRINT_TREE { println!(" OUSecureHashValidationDesc(0x18) - Size: {}", descriptor.size); };
|
||||
let secure_hash_validation_desc: OUSecureHashValidationDesc = reader.read_be()?;
|
||||
_secure_hash = Some(secure_hash_validation_desc.hash);
|
||||
if CONFIG_PRINT_TREE {
|
||||
println!(" Mode: {}", secure_hash_validation_desc.mode);
|
||||
println!(" Hash size: {}", secure_hash_validation_desc.hash_size);
|
||||
println!(" Hash: {}", hex::encode(&secure_hash_validation_desc.hash));
|
||||
};
|
||||
//secure_hash = Some(secure_hash_validation_desc.hash);
|
||||
}
|
||||
else {
|
||||
//type not implemented, ignore the data
|
||||
if CONFIG_PRINT_TREE { println!(" Unimplemented descriptor(0x{:02x}) - Size: {}", descriptor.tag, descriptor.size); };
|
||||
let _descriptor_data = common::read_exact(&mut reader, descriptor.size as usize);
|
||||
}
|
||||
}
|
||||
@@ -160,7 +199,9 @@ pub fn parse_ouith_blob(blob: &[u8]) -> Result<(Vec<MSDItem>, Option<OUSWImageVe
|
||||
//OUGroupInfoDesc
|
||||
let group_info_descriptor: DescriptorHeader = reader.read_be()?;
|
||||
if group_info_descriptor.tag != 0x13 {return Err(format!("Unexpected descriptor type in OUUpgradeItemDesc, Expected: 0x13, Got: 0x{:02x}!", group_info_descriptor.tag).into())}
|
||||
let _group_id: u32 = reader.read_be()?;
|
||||
if CONFIG_PRINT_TREE { println!(" OUGroupInfoDesc(0x13) - Size: {}", group_info_descriptor.size); };
|
||||
let group_id: u32 = reader.read_be()?;
|
||||
if CONFIG_PRINT_TREE { println!(" Group ID: {}", group_id); };
|
||||
|
||||
//create the msd item with all infos
|
||||
let msd_item = MSDItem {
|
||||
@@ -182,19 +223,36 @@ pub fn parse_ouith_blob(blob: &[u8]) -> Result<(Vec<MSDItem>, Option<OUSWImageVe
|
||||
}
|
||||
|
||||
else if top_descriptor.tag == 0x02 {
|
||||
let _group_desc: OUGroupDesc = reader.read_be()?;
|
||||
if CONFIG_PRINT_TREE { println!("OUGroupDesc(0x02) - Size: {}", top_descriptor.size); };
|
||||
let group_desc: OUGroupDesc = reader.read_be()?;
|
||||
if CONFIG_PRINT_TREE {
|
||||
println!(" Group ID: {}", group_desc.group_id);
|
||||
println!(" Field 2: {}", group_desc.field_2);
|
||||
println!(" Field 3: {}", group_desc.field_3);
|
||||
};
|
||||
|
||||
//OUGroupDesc REQUIRES one of: OUSWImageVersionDesc(0x09), OUSWImageVersionExDesc(0x19), OUOptionalDataVersionDesc(0x14), OUFirmwareVersionDesc(0x15). OPTIONALLY it can also have OUDependenciesDesc
|
||||
//MSD files seem to exclusively use OUSWImageVersionExDesc
|
||||
let version_descriptor: DescriptorHeader = reader.read_be()?;
|
||||
if ![0x09, 0x19, 0x14, 0x15].contains(&version_descriptor.tag) {return Err(format!("Unexpected descriptor type in OUGroupDesc, Expected: one of 0x09, 0x19, 0x14, 0x15, Got: 0x{:02x}!", version_descriptor.tag).into())}
|
||||
if version_descriptor.tag == 0x19 {
|
||||
if CONFIG_PRINT_TREE { println!(" OUSWImageVersionExDesc(0x12) - Size: {}", version_descriptor.size); };
|
||||
let sw_image_version_ex_desc: OUSWImageVersionExDesc = reader.read_be()?;
|
||||
if CONFIG_PRINT_TREE {
|
||||
println!(" Name lenght: {}", sw_image_version_ex_desc.name_len);
|
||||
println!(" Name: {}", sw_image_version_ex_desc.name());
|
||||
println!(" Major version: {}", sw_image_version_ex_desc.major_ver);
|
||||
println!(" Minor version: {}", sw_image_version_ex_desc.minor_ver);
|
||||
println!(" Date year: {}", sw_image_version_ex_desc.date_year);
|
||||
println!(" Date month: {}", sw_image_version_ex_desc.date_month);
|
||||
println!(" Date day: {}", sw_image_version_ex_desc.date_day);
|
||||
};
|
||||
|
||||
info = Some(sw_image_version_ex_desc);
|
||||
}
|
||||
else {
|
||||
//type not implemented, ignore the data
|
||||
if CONFIG_PRINT_TREE { println!(" Unimplemented descriptor(0x{:02x}) - Size: {}", version_descriptor.tag, version_descriptor.size); };
|
||||
let _descriptor_data = common::read_exact(&mut reader, version_descriptor.size as usize);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,269 @@
|
||||
//MAIN CODE: https://github.com/theubusu/msd_OUITH_parser
|
||||
|
||||
use std::io::{Cursor};
|
||||
use binrw::{BinRead, BinReaderExt};
|
||||
|
||||
use crate::utils::common;
|
||||
|
||||
// whether to print the tree
|
||||
static CONFIG_PRINT_TREE: bool = false;
|
||||
|
||||
#[derive(BinRead)]
|
||||
struct DescriptorHeader {
|
||||
_flag: u8,
|
||||
size: u32, //size includes the next TAG value
|
||||
tag: u32,
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
struct OUUpgradeItemDesc {
|
||||
_flag: u8,
|
||||
item_id: u32,
|
||||
unk_flag: u8,
|
||||
original_size: u32,
|
||||
processed_size: u32,
|
||||
unk: u16,
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
struct OUDataProcessingDesc {
|
||||
_flag: u8,
|
||||
subdesc_count: u32,
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
struct OUGroupInfoDesc {
|
||||
_flag: u8,
|
||||
group_id: u32,
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
struct OUCRC32ValidationDesc {
|
||||
_flag: u8,
|
||||
crc32: u32,
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
struct OURSAValidationDesc {
|
||||
_flag: u8,
|
||||
signature_size: u16,
|
||||
public_key_id: u8,
|
||||
#[br(count = signature_size)] signature: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
struct OUAESEncryptionDesc {
|
||||
_flag: u8,
|
||||
private_key_id: u32,
|
||||
salt_size: u8,
|
||||
#[br(count = salt_size)] salt: Vec<u8>,
|
||||
processed_size: u32,
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
struct OUGroupDesc {
|
||||
_flag: u8,
|
||||
group_id: u32,
|
||||
unknown: u16,
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
pub struct OUSWImageVersionDesc {
|
||||
pub _flag: u8,
|
||||
pub name_len: u8,
|
||||
#[br(count = name_len)] pub name_bytes: Vec<u8>,
|
||||
pub major_ver: u16,
|
||||
pub minor_ver: u16,
|
||||
pub date_year: u16,
|
||||
pub date_month: u8,
|
||||
pub date_day: u8,
|
||||
}
|
||||
impl OUSWImageVersionDesc {
|
||||
pub fn name(&self) -> String {
|
||||
common::string_from_bytes(&self.name_bytes)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
struct OUPartitionVersionDesc {
|
||||
_flag: u8,
|
||||
name_len: u8,
|
||||
#[br(count = name_len)] name_bytes: Vec<u8>,
|
||||
version: u16,
|
||||
}
|
||||
impl OUPartitionVersionDesc {
|
||||
pub fn name(&self) -> String {
|
||||
common::string_from_bytes(&self.name_bytes)
|
||||
}
|
||||
}
|
||||
|
||||
//this is a struct that will communicate all values important when parsing the MSD file.
|
||||
pub struct MSDItem {
|
||||
pub item_id: u32,
|
||||
pub name: String,
|
||||
|
||||
//pub crc32_hash: Option<u32>,
|
||||
pub aes_encryption: bool,
|
||||
pub aes_salt: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
pub fn parse_blob_1_8(blob: &[u8]) -> Result<(Vec<MSDItem>, Option<OUSWImageVersionDesc>), Box<dyn std::error::Error>> {
|
||||
let mut reader = Cursor::new(blob);
|
||||
let mut items: Vec<MSDItem> = Vec::new();
|
||||
let mut info: Option<OUSWImageVersionDesc> = None;
|
||||
|
||||
let _signature = common::read_exact(&mut reader, 256)?; //signature included at the beginning of blob in MSD file
|
||||
let magic = common::read_exact(&mut reader, 51)?;
|
||||
if magic != b"Tizen Software Upgrade Tree Binary Format ver. 1.8\x00" {
|
||||
return Err(format!("Invalid tree magic!").into())
|
||||
}
|
||||
|
||||
let top_level_descriptor_count: u32 = reader.read_be()?; //BIG ENDIAN
|
||||
if CONFIG_PRINT_TREE { println!("\nTop level descriptor count: {}", top_level_descriptor_count); };
|
||||
|
||||
for _i in 0..top_level_descriptor_count {
|
||||
//parse top level descriptor. it can only be ID 1(OUUpgradeItemDesc) or 2(OUGroupDesc)
|
||||
let top_descriptor: DescriptorHeader = reader.read_be()?;
|
||||
if top_descriptor.tag == 0x01 {
|
||||
if CONFIG_PRINT_TREE { println!("OUUpgradeItemDesc(0x01) - Size: {}", top_descriptor.size); };
|
||||
let upgrade_item_desc: OUUpgradeItemDesc = reader.read_be()?;
|
||||
if CONFIG_PRINT_TREE {
|
||||
println!(" Item ID: {}", upgrade_item_desc.item_id);
|
||||
println!(" Unknown flag: {}", upgrade_item_desc.unk_flag);
|
||||
println!(" Original size: {}", upgrade_item_desc.original_size);
|
||||
println!(" Processed size: {}", upgrade_item_desc.processed_size);
|
||||
println!(" Unknown: {}", upgrade_item_desc.unk);
|
||||
};
|
||||
|
||||
let subdesc_count: u32 = reader.read_le()?; //LITTLE ENDIAN??
|
||||
if CONFIG_PRINT_TREE { println!(" Subdescriptor count: {}", subdesc_count); };
|
||||
|
||||
let mut name: Option<String> = None;
|
||||
//let mut crc32_hash: Option<u32> = None;
|
||||
let mut aes_encryption = false;
|
||||
let mut aes_salt: Option<Vec<u8>> = None;
|
||||
|
||||
for _i in 0..subdesc_count {
|
||||
let sub_descriptor: DescriptorHeader = reader.read_be()?;
|
||||
if sub_descriptor.tag == 0x0A {
|
||||
if CONFIG_PRINT_TREE { println!(" OUPartitionVersionDesc(0x0A) - Size: {}", sub_descriptor.size); };
|
||||
let partition_version_desc: OUPartitionVersionDesc = reader.read_be()?;
|
||||
if CONFIG_PRINT_TREE {
|
||||
println!(" Name lenght: {}", partition_version_desc.name_len);
|
||||
println!(" Name: {}", partition_version_desc.name());
|
||||
println!(" Version: {}", partition_version_desc.version);
|
||||
};
|
||||
|
||||
name = Some(partition_version_desc.name());
|
||||
}
|
||||
else if sub_descriptor.tag == 0x07 {
|
||||
if CONFIG_PRINT_TREE { println!(" OUDataProcessingDesc(0x07) - Size: {}", sub_descriptor.size); };
|
||||
let data_processing_desc: OUDataProcessingDesc = reader.read_le()?; //LITTLE ENDIAN??
|
||||
if CONFIG_PRINT_TREE { println!(" Subdescriptor count: {}", data_processing_desc.subdesc_count); };
|
||||
|
||||
for _i in 0..data_processing_desc.subdesc_count {
|
||||
let data_processing_sub_desc: DescriptorHeader = reader.read_be()?;
|
||||
if data_processing_sub_desc.tag == 0x12 {
|
||||
if CONFIG_PRINT_TREE { println!(" OUCRC32ValidationDesc(0x12) - Size: {}", data_processing_sub_desc.size); };
|
||||
let crc32_validation_desc: OUCRC32ValidationDesc = reader.read_be()?;
|
||||
if CONFIG_PRINT_TREE { println!(" CRC32: {:02x}", crc32_validation_desc.crc32); };
|
||||
|
||||
//crc32_hash = Some(crc32_validation_desc.crc32);
|
||||
}
|
||||
else if data_processing_sub_desc.tag == 0x10 {
|
||||
if CONFIG_PRINT_TREE { println!(" OURSAValidationDesc(0x10) - Size: {}", data_processing_sub_desc.size); };
|
||||
let rsa_validation_desc: OURSAValidationDesc = reader.read_be()?;
|
||||
if CONFIG_PRINT_TREE {
|
||||
println!(" Signature size: {}", rsa_validation_desc.signature_size);
|
||||
println!(" Public key ID: {}", rsa_validation_desc.public_key_id);
|
||||
println!(" Signature: {}", hex::encode(&rsa_validation_desc.signature));
|
||||
};
|
||||
}
|
||||
else if data_processing_sub_desc.tag == 0x0E {
|
||||
if CONFIG_PRINT_TREE { println!(" OUAESEncryptionDesc(0x0E) - Size: {}", data_processing_sub_desc.size); };
|
||||
let aes_encryption_desc: OUAESEncryptionDesc = reader.read_be()?;
|
||||
if CONFIG_PRINT_TREE {
|
||||
println!(" Private key ID: {}", aes_encryption_desc.private_key_id);
|
||||
println!(" Salt size: {}", aes_encryption_desc.salt_size);
|
||||
println!(" Salt: {}", hex::encode(&aes_encryption_desc.salt));
|
||||
println!(" Processed size: {}", aes_encryption_desc.processed_size);
|
||||
};
|
||||
|
||||
aes_encryption = true;
|
||||
aes_salt = Some(aes_encryption_desc.salt);
|
||||
}
|
||||
else {
|
||||
if CONFIG_PRINT_TREE { println!(" Unimplemented Descriptor(0x{:02x}) - Size: {}", data_processing_sub_desc.tag, data_processing_sub_desc.size); };
|
||||
let _ = common::read_exact(&mut reader, data_processing_sub_desc.size as usize - 4)?;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else if sub_descriptor.tag == 0x13 {
|
||||
if CONFIG_PRINT_TREE { println!(" OUGroupInfoDesc(0x13) - Size: {}", sub_descriptor.size); };
|
||||
let group_info_desc: OUGroupInfoDesc = reader.read_be()?;
|
||||
if CONFIG_PRINT_TREE { println!(" Group ID: {}", group_info_desc.group_id); };
|
||||
}
|
||||
else {
|
||||
if CONFIG_PRINT_TREE { println!(" Unimplemented Descriptor(0x{:02x}) - Size: {}", sub_descriptor.tag, sub_descriptor.size); };
|
||||
let _ = common::read_exact(&mut reader, sub_descriptor.size as usize - 4)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(name) = name {
|
||||
//create the msd item with all infos
|
||||
let msd_item = MSDItem {
|
||||
item_id: upgrade_item_desc.item_id,
|
||||
name: name,
|
||||
|
||||
//crc32_hash: crc32_hash,
|
||||
aes_encryption: aes_encryption,
|
||||
aes_salt: aes_salt,
|
||||
};
|
||||
items.push(msd_item);
|
||||
} else {
|
||||
//if no name found panic because it is required
|
||||
return Err(format!("Could not retrieve required Name for item ID {}", upgrade_item_desc.item_id).into());
|
||||
}
|
||||
}
|
||||
else if top_descriptor.tag == 0x02 {
|
||||
if CONFIG_PRINT_TREE { println!("OUGroupDesc(0x02) - Size: {}", top_descriptor.size); };
|
||||
let group_desc: OUGroupDesc = reader.read_be()?;
|
||||
if CONFIG_PRINT_TREE {
|
||||
println!(" Group ID: {}", group_desc.group_id);
|
||||
println!(" Unknown: {}", group_desc.unknown);
|
||||
};
|
||||
|
||||
let subdesc_count: u32 = reader.read_le()?; //LITTLE ENDIAN??
|
||||
if CONFIG_PRINT_TREE { println!(" Subdescriptor count: {}", subdesc_count); };
|
||||
|
||||
for _i in 0..subdesc_count {
|
||||
let sub_descriptor: DescriptorHeader = reader.read_be()?;
|
||||
if sub_descriptor.tag == 0x19 {
|
||||
if CONFIG_PRINT_TREE { println!(" OUSWImageVersionDesc(0x19) - Size: {}", sub_descriptor.size); };
|
||||
let sw_image_version_desc: OUSWImageVersionDesc = reader.read_be()?;
|
||||
if CONFIG_PRINT_TREE {
|
||||
println!(" Name lenght: {}", sw_image_version_desc.name_len);
|
||||
println!(" Name: {}", sw_image_version_desc.name());
|
||||
println!(" Major ver: {}", sw_image_version_desc.major_ver);
|
||||
println!(" Minor ver: {}", sw_image_version_desc.minor_ver);
|
||||
println!(" Year: {}", sw_image_version_desc.date_year);
|
||||
println!(" Month: {}", sw_image_version_desc.date_month);
|
||||
println!(" Day: {}", sw_image_version_desc.date_day);
|
||||
};
|
||||
|
||||
info = Some(sw_image_version_desc);
|
||||
} else {
|
||||
if CONFIG_PRINT_TREE { println!(" Unimplemented Descriptor (0x{:02x}) - Size: {}", sub_descriptor.tag, sub_descriptor.size); };
|
||||
let _ = common::read_exact(&mut reader, sub_descriptor.size as usize - 4)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
return Err(format!("Unexpected top level descriptor type 0x{:02x}!", top_descriptor.tag).into());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Ok((items, info)) //finally, it will return a list of MSD items and info about image if it was collected.
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
//MAIN CODE: https://github.com/theubusu/msd_OUITH_parser
|
||||
|
||||
use std::io::{Cursor};
|
||||
use binrw::{BinRead, BinReaderExt};
|
||||
|
||||
use crate::utils::common;
|
||||
|
||||
// whether to print the tree
|
||||
static CONFIG_PRINT_TREE: bool = false;
|
||||
|
||||
#[derive(BinRead)]
|
||||
struct DescriptorHeader {
|
||||
_flag: u8,
|
||||
size: u32, //size includes the next TAG value
|
||||
tag: u32,
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
struct OUUpgradeItemDesc {
|
||||
_flag: u8,
|
||||
item_id: u32,
|
||||
unk_flag: u8,
|
||||
original_size: u64,
|
||||
processed_size: u64,
|
||||
unk: u16,
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
struct OUDataProcessingDesc {
|
||||
_flag: u8,
|
||||
subdesc_count: u32,
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
struct OUGroupInfoDesc {
|
||||
_flag: u8,
|
||||
group_id: u32,
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
struct OUCRC32ValidationDesc {
|
||||
_flag: u8,
|
||||
crc32: u32,
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
struct OURSAValidationDesc {
|
||||
_flag: u8,
|
||||
public_key_id: u32,
|
||||
signature_size: u16,
|
||||
#[br(count = signature_size)] signature: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
struct OUAESEncryptionDesc {
|
||||
_flag: u8,
|
||||
salt_size: u8,
|
||||
#[br(count = salt_size)] salt: Vec<u8>,
|
||||
processed_size: u64,
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
struct OUGroupDesc {
|
||||
_flag: u8,
|
||||
group_id: u32,
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
pub struct OUSWImageVersionDesc {
|
||||
pub _flag: u8,
|
||||
pub name_len: u8,
|
||||
#[br(count = name_len)] pub name_bytes: Vec<u8>,
|
||||
pub major_ver: u16,
|
||||
pub minor_ver: u16,
|
||||
}
|
||||
impl OUSWImageVersionDesc {
|
||||
pub fn name(&self) -> String {
|
||||
common::string_from_bytes(&self.name_bytes)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
struct OUPartitionVersionDesc {
|
||||
_flag: u8,
|
||||
name_len: u8,
|
||||
#[br(count = name_len)] name_bytes: Vec<u8>,
|
||||
}
|
||||
impl OUPartitionVersionDesc {
|
||||
pub fn name(&self) -> String {
|
||||
common::string_from_bytes(&self.name_bytes)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
struct OUSecureDowngradeDesc {
|
||||
_flag: u8,
|
||||
image_generation_date: u32,
|
||||
_unk1: u32,
|
||||
_unk2: u32,
|
||||
}
|
||||
|
||||
//this is a struct that will communicate all values important when parsing the MSD file.
|
||||
pub struct MSDItem {
|
||||
pub item_id: u32,
|
||||
pub name: String,
|
||||
|
||||
//pub crc32_hash: Option<u32>,
|
||||
pub aes_encryption: bool,
|
||||
pub aes_salt: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
pub fn parse_blob_1_9(blob: &[u8]) -> Result<(Vec<MSDItem>, Option<OUSWImageVersionDesc>), Box<dyn std::error::Error>> {
|
||||
let mut reader = Cursor::new(blob);
|
||||
let mut items: Vec<MSDItem> = Vec::new();
|
||||
let mut info: Option<OUSWImageVersionDesc> = None;
|
||||
|
||||
let _public_key_id: u32 = reader.read_le()?;
|
||||
let signature_size: u16 = reader.read_le()?;
|
||||
let _signature = common::read_exact(&mut reader, signature_size as usize)?; //signature included at the beginning of blob in MSD file
|
||||
|
||||
let magic = common::read_exact(&mut reader, 51)?;
|
||||
if magic != b"Tizen Software Upgrade Tree Binary Format ver. 1.9\x00" {
|
||||
return Err(format!("Invalid tree magic!").into())
|
||||
}
|
||||
|
||||
let top_level_descriptor_count: u32 = reader.read_le()?;
|
||||
if CONFIG_PRINT_TREE { println!("\nTop level descriptor count: {}", top_level_descriptor_count); };
|
||||
|
||||
for _i in 0..top_level_descriptor_count {
|
||||
//parse top level descriptor. it can only be ID 1(OUUpgradeItemDesc) or 2(OUGroupDesc) or 0x37(OUSecureDowngradeDesc)
|
||||
let top_descriptor: DescriptorHeader = reader.read_le()?;
|
||||
if top_descriptor.tag == 0x01 {
|
||||
if CONFIG_PRINT_TREE { println!("OUUpgradeItemDesc(0x01) - Size: {}", top_descriptor.size); };
|
||||
let upgrade_item_desc: OUUpgradeItemDesc = reader.read_le()?;
|
||||
if CONFIG_PRINT_TREE {
|
||||
println!(" Item ID: {}", upgrade_item_desc.item_id);
|
||||
println!(" Unknown flag: {}", upgrade_item_desc.unk_flag);
|
||||
println!(" Original size: {}", upgrade_item_desc.original_size);
|
||||
println!(" Processed size: {}", upgrade_item_desc.processed_size);
|
||||
println!(" Unknown: {}", upgrade_item_desc.unk);
|
||||
};
|
||||
|
||||
let subdesc_count: u32 = reader.read_le()?;
|
||||
if CONFIG_PRINT_TREE { println!(" Subdescriptor count: {}", subdesc_count); };
|
||||
|
||||
let mut name: Option<String> = None;
|
||||
//let mut crc32_hash: Option<u32> = None;
|
||||
let mut aes_encryption = false;
|
||||
let mut aes_salt: Option<Vec<u8>> = None;
|
||||
|
||||
for _i in 0..subdesc_count {
|
||||
let sub_descriptor: DescriptorHeader = reader.read_le()?;
|
||||
if sub_descriptor.tag == 0x0A {
|
||||
if CONFIG_PRINT_TREE { println!(" OUPartitionVersionDesc(0x0A) - Size: {}", sub_descriptor.size); };
|
||||
let partition_version_desc: OUPartitionVersionDesc = reader.read_le()?;
|
||||
if CONFIG_PRINT_TREE {
|
||||
println!(" Name lenght: {}", partition_version_desc.name_len);
|
||||
println!(" Name: {}", partition_version_desc.name());
|
||||
};
|
||||
|
||||
name = Some(partition_version_desc.name());
|
||||
}
|
||||
else if sub_descriptor.tag == 0x07 {
|
||||
if CONFIG_PRINT_TREE { println!(" OUDataProcessingDesc(0x07) - Size: {}", sub_descriptor.size); };
|
||||
let data_processing_desc: OUDataProcessingDesc = reader.read_le()?;
|
||||
if CONFIG_PRINT_TREE { println!(" Subdescriptor count: {}", data_processing_desc.subdesc_count); };
|
||||
|
||||
for _i in 0..data_processing_desc.subdesc_count {
|
||||
let data_processing_sub_desc: DescriptorHeader = reader.read_le()?;
|
||||
if data_processing_sub_desc.tag == 0x12 {
|
||||
if CONFIG_PRINT_TREE { println!(" OUCRC32ValidationDesc(0x12) - Size: {}", data_processing_sub_desc.size); };
|
||||
let crc32_validation_desc: OUCRC32ValidationDesc = reader.read_le()?;
|
||||
if CONFIG_PRINT_TREE { println!(" CRC32: {:02x}", crc32_validation_desc.crc32); };
|
||||
|
||||
//crc32_hash = Some(crc32_validation_desc.crc32);
|
||||
}
|
||||
else if data_processing_sub_desc.tag == 0x10 {
|
||||
if CONFIG_PRINT_TREE { println!(" OURSAValidationDesc(0x10) - Size: {}", data_processing_sub_desc.size); };
|
||||
let rsa_validation_desc: OURSAValidationDesc = reader.read_le()?;
|
||||
if CONFIG_PRINT_TREE {
|
||||
println!(" Signature size: {}", rsa_validation_desc.signature_size);
|
||||
println!(" Public key ID: {}", rsa_validation_desc.public_key_id);
|
||||
println!(" Signature: {}", hex::encode(&rsa_validation_desc.signature));
|
||||
};
|
||||
}
|
||||
else if data_processing_sub_desc.tag == 0x0E {
|
||||
if CONFIG_PRINT_TREE { println!(" OUAESEncryptionDesc(0x0E) - Size: {}", data_processing_sub_desc.size); };
|
||||
let aes_encryption_desc: OUAESEncryptionDesc = reader.read_le()?;
|
||||
if CONFIG_PRINT_TREE {
|
||||
println!(" Salt size: {}", aes_encryption_desc.salt_size);
|
||||
println!(" Salt: {}", hex::encode(&aes_encryption_desc.salt));
|
||||
println!(" Processed size: {}", aes_encryption_desc.processed_size);
|
||||
};
|
||||
|
||||
aes_encryption = true;
|
||||
aes_salt = Some(aes_encryption_desc.salt);
|
||||
}
|
||||
else {
|
||||
if CONFIG_PRINT_TREE { println!(" Unimplemented Descriptor(0x{:02x}) - Size: {}", data_processing_sub_desc.tag, data_processing_sub_desc.size); };
|
||||
let _ = common::read_exact(&mut reader, data_processing_sub_desc.size as usize - 4)?;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else if sub_descriptor.tag == 0x13 {
|
||||
if CONFIG_PRINT_TREE { println!(" OUGroupInfoDesc(0x13) - Size: {}", sub_descriptor.size); };
|
||||
let group_info_desc: OUGroupInfoDesc = reader.read_le()?;
|
||||
if CONFIG_PRINT_TREE { println!(" Group ID: {}", group_info_desc.group_id); };
|
||||
}
|
||||
else {
|
||||
if CONFIG_PRINT_TREE { println!(" Unimplemented Descriptor(0x{:02x}) - Size: {}", sub_descriptor.tag, sub_descriptor.size); };
|
||||
let _ = common::read_exact(&mut reader, sub_descriptor.size as usize - 4)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(name) = name {
|
||||
//create the msd item with all infos
|
||||
let msd_item = MSDItem {
|
||||
item_id: upgrade_item_desc.item_id,
|
||||
name: name,
|
||||
|
||||
//crc32_hash: crc32_hash,
|
||||
aes_encryption: aes_encryption,
|
||||
aes_salt: aes_salt,
|
||||
};
|
||||
items.push(msd_item);
|
||||
} else {
|
||||
//if no name found panic because it is required
|
||||
return Err(format!("Could not retrieve required Name for item ID {}", upgrade_item_desc.item_id).into());
|
||||
}
|
||||
}
|
||||
else if top_descriptor.tag == 0x02 {
|
||||
if CONFIG_PRINT_TREE { println!("OUGroupDesc(0x02) - Size: {}", top_descriptor.size); };
|
||||
let group_desc: OUGroupDesc = reader.read_le()?;
|
||||
if CONFIG_PRINT_TREE { println!(" Group ID: {}", group_desc.group_id); };
|
||||
|
||||
let subdesc_count: u32 = reader.read_le()?; //LITTLE ENDIAN??
|
||||
if CONFIG_PRINT_TREE { println!(" Subdescriptor count: {}", subdesc_count); };
|
||||
|
||||
for _i in 0..subdesc_count {
|
||||
let sub_descriptor: DescriptorHeader = reader.read_le()?;
|
||||
if sub_descriptor.tag == 0x19 {
|
||||
if CONFIG_PRINT_TREE { println!(" OUSWImageVersionDesc(0x19) - Size: {}", sub_descriptor.size); };
|
||||
let sw_image_version_desc: OUSWImageVersionDesc = reader.read_le()?;
|
||||
if CONFIG_PRINT_TREE {
|
||||
println!(" Name lenght: {}", sw_image_version_desc.name_len);
|
||||
println!(" Name: {}", sw_image_version_desc.name());
|
||||
println!(" Major ver: {}", sw_image_version_desc.major_ver);
|
||||
println!(" Minor ver: {}", sw_image_version_desc.minor_ver);
|
||||
};
|
||||
|
||||
info = Some(sw_image_version_desc);
|
||||
} else {
|
||||
if CONFIG_PRINT_TREE { println!(" Unimplemented Descriptor (0x{:02x}) - Size: {}", sub_descriptor.tag, sub_descriptor.size); };
|
||||
let _ = common::read_exact(&mut reader, sub_descriptor.size as usize - 4)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if top_descriptor.tag == 0x37 {
|
||||
if CONFIG_PRINT_TREE { println!("OUSecureDowngradeDesc(0x37) - Size: {}", top_descriptor.size); };
|
||||
let secure_downgrade_desc: OUSecureDowngradeDesc = reader.read_le()?;
|
||||
if CONFIG_PRINT_TREE { println!(" Image generation timestamp: {}", secure_downgrade_desc.image_generation_date); };
|
||||
}
|
||||
else {
|
||||
return Err(format!("Unexpected top level descriptor type 0x{:02x}!", top_descriptor.tag).into());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Ok((items, info)) //finally, it will return a list of MSD items and info about image if it was collected.
|
||||
}
|
||||
Reference in New Issue
Block a user