use crate::cli::{DecryptCmd, EncryptCmd, ShowMetaCmd};
use crate::utils::{decrypt_data, print_hex, u64_to_u8_array, xor_encrypt_decrypt};
use std::fs::File;
use std::io::{copy, Error, ErrorKind, Read, Seek, SeekFrom, Write};
use std::mem;
#[derive(Debug, Clone)]
pub struct Header {
pub header: u64,
}
#[derive(Debug, Clone)]
pub struct Chunk {
pub size: u32,
pub r#type: u32,
pub data: Vec<u8>,
pub crc: u32,
}
#[derive(Debug, Clone)]
pub struct MetaChunk {
pub header: Header,
pub chk: Chunk,
pub offset: u64,
}
impl MetaChunk {
pub fn new(file: &mut File, suppress: bool) -> Result<MetaChunk, Error> {
let mut header = Header { header: 0 };
file.read_exact(unsafe { mem::transmute::<_, &mut [u8; 8]>(&mut header.header) })?;
let b_arr = u64_to_u8_array(header.header);
let offset = file.stream_position()?;
if &b_arr[1..4] != b"PNG" {
let _err = Error::new(ErrorKind::Other, "Not a valid PNG file!");
return Err(_err);
} else if !suppress {
println!("It is a valid PNG file. Let's process it! \n");
println!("\x1b[92m---- Header ----\x1b[0m");
print_hex(&b_arr, 0);
print!("\x1b[0m");
println!("\x1b[92m----- End ------\x1b[0m");
println!();
}
Ok(MetaChunk {
header,
chk: Chunk {
size: 0,
r#type: 0,
data: Vec::new(),
crc: 0,
},
offset,
})
}
pub fn process_image(&mut self, file: &mut File, c: &ShowMetaCmd) {
let mut start_position: usize = c.start_chunk;
let mut end_position: usize = c.end_chunk;
let mut _chunk_type = String::new();
let end_chunk_type = "IEND";
if c.read_end {
file.seek(SeekFrom::End(
(-(start_position as isize)).try_into().unwrap(),
))
.unwrap();
start_position = file.metadata().unwrap().len() as usize - c.nb_chunks;
end_position = file.metadata().unwrap().len() as usize - 1;
} else {
file.seek(SeekFrom::Start((start_position).try_into().unwrap()))
.unwrap();
if c.start_chunk > 8 {
self.offset = start_position as u64;
}
}
for (i, j) in (start_position..end_position).enumerate() {
_chunk_type = self.chunk_type_to_string();
if i >= c.nb_chunks || _chunk_type == end_chunk_type {
break;
}
self.read_chunk(file);
if !c.suppress {
println!("\x1b[92m---- Chunk #{} ----\x1b[0m", j);
println!("Offset: {:?}", self.offset);
println!("Size: {:?}", self.chk.size);
println!("CRC: {:x}", self.chk.crc);
print_hex(&self.chk.data, self.offset);
print!("\x1b[0m");
println!("\x1b[92m------- End -------\x1b[0m");
println!();
}
let _offset = self.get_offset(file);
}
}
fn get_offset<T: Read + Seek>(&mut self, file: &mut T) -> u64 {
let offset = file.stream_position().unwrap();
self.offset = offset;
offset
}
fn read_chunk<T: Read + Seek>(&mut self, file: &mut T) {
self.read_chunk_size(file);
self.read_chunk_type(file);
self.read_chunk_bytes(file, self.chk.size);
self.read_chunk_crc(file);
}
fn read_chunk_size<R: Read>(&mut self, file: &mut R) {
let mut size_bytes = [0; 4];
match file.read_exact(&mut size_bytes) {
Ok(_) => {
self.chk.size = u32::from_be_bytes(size_bytes);
if self.chk.size > 40 {
let min_non_zero_number = *size_bytes
.iter()
.filter(|&&byte| byte != 0)
.min_by(|a, b| a.cmp(b))
.unwrap_or(&0);
self.chk.size = min_non_zero_number as u32;
}
}
Err(_err) if _err.kind() == ErrorKind::UnexpectedEof => {
}
Err(_err) => {
}
}
}
fn read_chunk_type<R: Read>(&mut self, file: &mut R) {
let mut type_bytes = [0; 4];
match file.read_exact(&mut type_bytes) {
Ok(_) => {
self.chk.r#type = u32::from_be_bytes(type_bytes);
}
Err(_err) if _err.kind() == ErrorKind::UnexpectedEof => {
}
Err(_err) => {
}
}
}
fn read_chunk_bytes<T: Read + Seek>(&mut self, file: &mut T, len: u32) {
self.chk.data = vec![0; len as usize];
match file.read_exact(&mut self.chk.data) {
Ok(_) => {
}
Err(_err) if _err.kind() == ErrorKind::UnexpectedEof => {
self.chk
.data
.truncate(file.stream_position().unwrap() as usize);
}
Err(_err) => {
}
}
}
fn read_chunk_crc<R: Read>(&mut self, file: &mut R) {
let mut crc_bytes = [0; 4];
match file.read_exact(&mut crc_bytes) {
Ok(_) => {
self.chk.crc = u32::from_be_bytes(crc_bytes);
}
Err(_err) if _err.kind() == ErrorKind::UnexpectedEof => {
}
Err(_err) => {
}
}
}
fn chunk_type_to_string(&self) -> String {
String::from_utf8_lossy(&self.chk.r#type.to_be_bytes()).to_string()
}
fn marshal_data(&self) -> Vec<u8> {
let mut bytes_msb = Vec::new();
bytes_msb.push(self.chk.data.len() as u8);
bytes_msb.write_all(&self.chk.r#type.to_be_bytes()).unwrap();
bytes_msb.write_all(&self.chk.data).unwrap();
bytes_msb.write_all(&self.chk.crc.to_be_bytes()).unwrap();
bytes_msb
}
pub fn write_encrypted_data<R: Read + Seek, W: Write>(
&mut self,
r: &mut R,
c: &EncryptCmd,
mut w: W,
) {
let b_arr = u64_to_u8_array(self.header.header);
w.write_all(&b_arr).unwrap();
let mut offset = c.offset;
let encrypted_data = self.chk.data.clone();
let encrypted_data_len = self.chk.data.len();
let encrypted_data_crc = self.chk.crc;
let init_position = r.stream_position().unwrap();
if offset == 9999999999 {
offset = self.find_iend_offset(r);
r.seek(SeekFrom::Start(init_position)).unwrap();
}
self.chk.data = encrypted_data.clone();
self.chk.size = encrypted_data_len as u32;
self.chk.crc = encrypted_data_crc;
if !c.suppress {
println!("\x1b[92m------- Chunk -------\x1b[0m");
println!("Offset: {:?}", offset);
println!("Size: {:?}", encrypted_data_len);
println!("CRC: {:x}", encrypted_data_crc);
print_hex(&encrypted_data, offset.try_into().unwrap());
print!("\x1b[0m");
println!("\x1b[92m-------- End --------\x1b[0m");
println!();
}
let mut buff = vec![0; offset - 8];
buff.resize(&offset - 8, 0);
r.read_exact(&mut buff).unwrap();
w.write_all(&buff).unwrap();
let data: Vec<u8> = self.marshal_data();
w.write_all(&data).unwrap();
copy(r, &mut w).unwrap();
println!(
"Your payload has been encrypted and written at offset {} successfully!",
offset
);
}
pub fn write_decrypted_data<R: Read + Seek, W: Write>(
&mut self,
r: &mut R,
c: &DecryptCmd,
mut w: W,
) {
let b_arr = u64_to_u8_array(self.header.header);
w.write_all(&b_arr).unwrap();
let mut offset = c.offset;
let init_position = r.stream_position().unwrap();
if offset == 9999999999 {
offset = self.find_iend_offset(r);
r.seek(SeekFrom::Start(init_position)).unwrap();
}
let mut buff = vec![0; offset - 8];
buff.resize(offset - 16, 0);
r.read_exact(&mut buff).unwrap();
w.write_all(&buff).unwrap();
self.offset = r.seek(SeekFrom::Current(5)).unwrap();
self.read_chunk(r);
let mut decrypted_data: Vec<u8> = vec![0];
match (*c.algorithm.to_lowercase()).into() {
"aes" => {
decrypted_data = decrypt_data(&c.key, &self.chk.data);
}
"xor" => {
decrypted_data = xor_encrypt_decrypt(&self.chk.data, &c.key);
}
_ => {}
}
let decoded_string = String::from_utf8_lossy(&decrypted_data);
let unpadded_string = decoded_string.trim_end_matches('\0');
if !c.suppress {
println!("\x1b[92m------- Chunk -------\x1b[0m");
println!("Offset: {:?}", self.offset);
println!("Size: {:?}", self.chk.size);
println!("CRC: {:x}", self.chk.crc);
print_hex(&decrypted_data, offset.try_into().unwrap());
print!("\x1b[0m");
println!("\x1b[92m-------- End --------\x1b[0m");
println!();
}
r.seek(SeekFrom::Current(self.chk.data.len().try_into().unwrap()))
.expect("Error seeking to offset");
println!(
"\x1b[38;5;7mYour decrypted secret is:\x1b[0m \x1b[38;5;214m{:?}\x1b[0m",
unpadded_string
);
copy(r, &mut w).unwrap();
}
pub fn find_file_length<T>(&mut self, file: &mut T) -> std::io::Result<u64>
where
T: Read + Seek,
{
let current_position = file.stream_position()?;
let file_length = file.seek(SeekFrom::End(0))?;
file.seek(SeekFrom::Start(current_position))?;
Ok(file_length)
}
fn find_iend_offset<R>(&mut self, r: &mut R) -> usize
where
R: Seek + Read,
{
let mut iend_offset = 999;
let end_chunk_type = "IEND";
while iend_offset < self.find_file_length(r).unwrap() {
iend_offset = self.get_offset(r);
self.read_chunk(r);
let chunk_type = self.chunk_type_to_string();
if chunk_type == end_chunk_type {
break;
}
}
(iend_offset - 11) as usize
}
}