diff --git a/Cargo.lock b/Cargo.lock index 8303af649833e900600639694ea8f56dc7ebef5b..c15f9bc1a3a5103c8c953a6793261bfd9d1ce1a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -72,6 +72,7 @@ version = "0.1.0" dependencies = [ "byteorder", "clap", + "crc", "sha2", "tempfile", "thiserror", @@ -97,9 +98,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.2.7" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34d21f9bf1b425d2968943631ec91202fe5e837264063503708b83013f8fc938" +checksum = "93aae7a4192245f70fe75dd9157fc7b4a5bf53e88d30bd4396f7d8f9284d5acc" dependencies = [ "clap_builder", "clap_derive", @@ -108,9 +109,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.2.7" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914c8c79fb560f238ef6429439a30023c862f7a28e688c58f7203f12b29970bd" +checksum = "4f423e341edefb78c9caba2d9c7f7687d0e72e89df3ce3394554754393ac3990" dependencies = [ "anstream", "anstyle", @@ -121,9 +122,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.2.0" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" +checksum = "191d9573962933b4027f932c600cd252ce27a8ad5979418fe78e43c07996f27b" dependencies = [ "heck", "proc-macro2", @@ -133,9 +134,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" [[package]] name = "colorchoice" @@ -152,6 +153,21 @@ dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" + [[package]] name = "crypto-common" version = "0.1.6" @@ -164,9 +180,9 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", @@ -235,9 +251,9 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ "hermit-abi", "libc", @@ -264,9 +280,9 @@ checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" [[package]] name = "linux-raw-sys" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "once_cell" @@ -276,18 +292,18 @@ checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" dependencies = [ "proc-macro2", ] @@ -334,9 +350,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "2.0.15" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" dependencies = [ "proc-macro2", "quote", @@ -384,9 +400,9 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" [[package]] name = "utf8parse" diff --git a/Cargo.toml b/Cargo.toml index 71f03176cf11123a4a889dd10036ed137101cc31..bd0c0945b4107598d9dc09e213cdd293dbc1afcd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [dependencies] byteorder = "1.4.3" clap = { version = "4.2.7", features = ["derive"] } +crc = "3.0.1" sha2 = "0.10.6" thiserror = "1.0.40" diff --git a/src/lib.rs b/src/lib.rs index d10acdb6b78ff0a2f44d68c1571bd48eb6f8fc84..d1e6b58dc662be03a9e74a9bec7e12096ef9d0c6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,12 @@ -use byteorder::{BigEndian, LittleEndian, ReadBytesExt}; +use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt}; use sha2::{Digest, Sha256}; use std::fs::File; -use std::io::{self, Read, Seek, SeekFrom}; +use std::io::{self, Read, Seek, SeekFrom, Write}; const HEAD_LENGTH: u64 = 0x160; const HEAD_MAGIC: u32 = 0x42464e50; const FLASH_MAGIC: u32 = 0x46434647; -const CLK_MAGIC: u32 = 0x50434647; +const CLOCK_MAGIC: u32 = 0x50434647; #[derive(thiserror::Error, Debug)] pub enum Error { @@ -30,9 +30,22 @@ pub enum Error { Sha256Checksum, } -pub type Result = core::result::Result<(), Error>; +/// Process operations. +pub struct Operations { + /// Refill hash value of image body into header, or None if not needed. + /// + /// Should include 32 bytes for sha256 algorithm. + pub refill_hash: Option>, + /// Refill CRC32 value of header, None if not needed. + pub refill_header_crc: Option, +} + +pub type Result = core::result::Result; -pub fn process(f: &mut File) -> Result { +/// Check source file without modifying, returning suggested operations. +/// +/// File `f` should be readable, but not writable. +pub fn check(f: &mut File) -> Result { let file_length = f.metadata()?.len(); f.seek(SeekFrom::Start(0x00))?; @@ -57,7 +70,7 @@ pub fn process(f: &mut File) -> Result { f.seek(SeekFrom::Start(0x64))?; let clk_magic = f.read_u32::()?; - if clk_magic != CLK_MAGIC { + if clk_magic != CLOCK_MAGIC { return Err(Error::ClockConfigMagic); } @@ -75,12 +88,12 @@ pub fn process(f: &mut File) -> Result { }); } - //read hash values from file + // read hash values from file f.seek(SeekFrom::Start(0x90))?; - let mut hash = vec![0; 32]; - let _ = f.read(&mut hash)?; + let mut read_hash = vec![0; 32]; + let _ = f.read(&mut read_hash)?; - //calculate hash + // calculate hash f.seek(SeekFrom::Start(group_image_offset as u64))?; let mut hasher = Sha256::new(); let mut buffer = vec![0; img_len_cnt as usize]; @@ -92,24 +105,53 @@ pub fn process(f: &mut File) -> Result { hasher.update(&buffer[..n]); } - let hash2 = hasher.finalize(); - let mut array = [0u8; 32]; - array.copy_from_slice(&hash2); - let vec1 = Vec::from(array); - - if vec1 != hash { - let vec2: Vec = vec![0xdeadbeef, 0, 0, 0, 0, 0, 0, 0] - .iter() - .map(|x: &u32| *x as u8) - .collect(); - let vec3: Vec = vec![0xdeadbeef; 8].iter().map(|x: &u32| *x as u8).collect(); - if hash != vec2 && hash != vec3 { + let calculated_hash = &hasher.finalize()[..]; + + let refill_hash = if calculated_hash != read_hash { + let mut vec2 = vec![0u8; 32]; + vec2[..4].copy_from_slice(&[0xef, 0xbe, 0xad, 0xde]); + let mut vec3 = vec![0u8; 32]; + for i in 0..8 { + vec3[4 * i..4 * (i + 1)].copy_from_slice(&[0xef, 0xbe, 0xad, 0xde]); + } + if read_hash != vec2 && read_hash != vec3 { return Err(Error::Sha256Checksum); } - } + Some(Vec::from(calculated_hash)) + } else { + // source image hash is correct, do not need to fill + None + }; - let _ = f.read(&mut buffer)?; - println!("image content: {:?}", buffer); + f.seek(SeekFrom::Start(0x00))?; + let mut buf = vec![0u8; 0x15C]; + let _ = f.read(&mut buf)?; + let calculated_header_crc = crc::Crc::::new(&crc::CRC_32_ISO_HDLC).checksum(&buf); + + f.seek(SeekFrom::Start(0x15C))?; + let read_head_crc = f.read_u32::()?; + + let refill_header_crc = if read_head_crc != calculated_header_crc || refill_hash.is_some() { + Some(calculated_header_crc) + } else { + None + }; + + Ok(Operations { + refill_hash, + refill_header_crc, + }) +} +/// Process target file from operations. +pub fn process(f: &mut File, ops: &Operations) -> Result<()> { + if let Some(hash_to_fill) = &ops.refill_hash { + f.seek(SeekFrom::Start(0x90))?; + f.write(&hash_to_fill[..32])?; + } + if let Some(header_crc_to_fill) = &ops.refill_header_crc { + f.seek(SeekFrom::Start(0x15C))?; + f.write_u32::(*header_crc_to_fill)?; + } Ok(()) } diff --git a/src/main.rs b/src/main.rs index 1986cb7cc73e6c82961ee3695f3550998c9fa28f..fdd282b34e96e4a6bbdac21c27eaba2e2ed1e941 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ use blri::Error; use clap::Parser; -use std::fs::File; +use std::fs::{self, File}; /// Bouffalo ROM image helper #[derive(Parser, Debug)] @@ -15,23 +15,64 @@ struct Args { fn main() { let args = Args::parse(); - let mut f = File::open(args.input).expect("open file"); - - match blri::process(&mut f) { - Ok(_) => {println!("success");}, - Err(e) => match e { - Error::MagicNumber { wrong_magic } => println!("error: incorrect magic number {wrong_magic}!"), - Error::HeadLength { wrong_length } => - println!("File is too short to include an image header, it only includes {wrong_length} bytes"), - Error::FlashConfigMagic => println!("error: incorrect flash config magic!"), - Error::ClockConfigMagic => println!("error: incorrect clock config magic!"), - Error::ImageOffsetOverflow { file_length, wrong_image_offset, wrong_image_length } => - println!("error: file length is only {}, but offset is {} and image length is {}", file_length, wrong_image_offset, wrong_image_length), - Error::Sha256Checksum => println!("error: Sha256 verification failed!"), - Error::Io(source) => println!("error: io error! {:?}", source) - } - } - - // println!("Input file name: {}!", args.input); - // println!("Output file name: {:?}!", args.output); + let mut f_in = File::open(&args.input).expect("open input file"); + + let ops = match blri::check(&mut f_in) { + Ok(ops) => ops, + Err(e) => match e { + Error::MagicNumber { wrong_magic } => { + println!("error: incorrect magic number {wrong_magic}!"); + return; + } + Error::HeadLength { wrong_length } => { + println!("File is too short to include an image header, it only includes {wrong_length} bytes"); + return; + } + Error::FlashConfigMagic => { + println!("error: incorrect flash config magic!"); + return; + } + Error::ClockConfigMagic => { + println!("error: incorrect clock config magic!"); + return; + } + Error::ImageOffsetOverflow { + file_length, + wrong_image_offset, + wrong_image_length, + } => { + println!( + "error: file length is only {}, but offset is {} and image length is {}", + file_length, wrong_image_offset, wrong_image_length + ); + return; + } + Error::Sha256Checksum => { + println!("error: wrong sha256 verification."); + return; + } + Error::Io(source) => { + println!("error: io error! {:?}", source); + return; + } + }, + }; + + let output = args.output.clone().unwrap_or(args.input.clone()); + + if output != args.input { + fs::copy(&args.input, &output).expect("copy input to output"); + } + + // release input file + drop(f_in); + + // open output file as writeable + let mut f_out = File::options() + .write(true) + .create(true) + .open(output) + .expect("open output file"); + + blri::process(&mut f_out, &ops).expect("process file"); } diff --git a/tests/file_process.rs b/tests/file_check.rs similarity index 94% rename from tests/file_process.rs rename to tests/file_check.rs index 5ebf1c827bcf782efb5d7ccc1725789dddf7fbe0..65454b85187961d5a099ce53cb7277d13588d70f 100644 --- a/tests/file_process.rs +++ b/tests/file_check.rs @@ -10,7 +10,7 @@ fn error_magic_number() { f.seek(SeekFrom::Start(0x0)).expect("seek to magic number"); f.write_all(&[0x11, 0x22, 0x33, 0x44]) .expect("prepare wrong magic number"); - let ans = blri::process(&mut f); + let ans = blri::check(&mut f); if let Err(Error::MagicNumber { wrong_magic }) = ans { assert_eq!(wrong_magic, 0x11223344); } else {