Skip to content

Commit

Permalink
Add the ability for the CLI to "guess" the version of the codec used …
Browse files Browse the repository at this point in the history
…to encode the file.

Note that, when it reads a value larger than the current maximum version, it will assume it is a v1 file for legacy reasons.

Edited the codecs to no longer validate the version when decoding. This will allow people to tamper with the header (for whateve reason) and still successfully decode the file.
  • Loading branch information
sciguyryan committed Jan 29, 2025
1 parent fc26b72 commit 0925bc2
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 18 deletions.
6 changes: 6 additions & 0 deletions psistega3-cli/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ pub enum Error {
InvalidVersion,
/// The supplied passwords did not match.
PasswordMismatch,
/// Version guessing is not supported for encoding files.
NoVersionGuessing,
/// Version guessing failed to find a valid version.
VersionGuessingFailed,
}

impl fmt::Display for Error {
Expand All @@ -30,6 +34,8 @@ impl fmt::Display for Error {
Error::PasswordMismatch => {
"The entered passwords did not match. Please check and try again."
}
Error::NoVersionGuessing => "Version guessing is not supported for encoding files.",
Error::VersionGuessingFailed => "Version guessing failed to find a valid version.",
})
}
}
Expand Down
57 changes: 52 additions & 5 deletions psistega3-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,23 @@ mod error;

use crate::error::{Error, Result};

use psistega3_core::codecs::codec::{Codec, Config};
use psistega3_core::codecs::v1::StegaV1;
use psistega3_core::codecs::v2::StegaV2;
use psistega3_core::version::*;
use psistega3_core::{
codecs::{
codec::{Codec, Config},
v1::StegaV1,
v2::StegaV2,
},
utilities::png_utils::{self, PngChunkType},
version::*,
};

use simple_logger::SimpleLogger;
use std::{convert::TryFrom, env, io::stdin};

/// The prompt for confirming a yes/no option.
const CONFIRM_PROMPT: &str = "Are you sure you wish to enable this feature?";
/// The current highest codec version.
const CURRENT_MAX_VERSION: u8 = 0x2;

//ooneporlygs

Expand Down Expand Up @@ -86,7 +93,21 @@ fn main() {
if needs_codec {
let mut codec_version: Option<Version> = None;
if &args[2] == "-v" {
let version = &args[3];
let mut version = args[3].clone();

// Have we been asked to guess the version, based on the file itself?
if version.to_uppercase() == "GUESS" {
if action_type == ActionType::Encode {
show_abort_message(Error::NoVersionGuessing);
}

if let Some(v) = read_bkgd_chunk(&args[5]) {
version = v;
} else {
show_abort_message(Error::VersionGuessingFailed);
}
}

if let Ok(v) = version.parse::<u8>() {
if let Ok(cv) = Version::try_from(v) {
codec_version = Some(cv);
Expand Down Expand Up @@ -136,6 +157,26 @@ fn main() {
}
}

fn read_bkgd_chunk(path: &str) -> Option<String> {
let chunk = png_utils::read_chunk_raw(path, PngChunkType::Bkgd)?;

// We have a bKGD chunk to process!
let data = png_utils::get_chunk_data(&chunk)?;

if data.len() < 6 {
return None;
}

// This could be a legacy file, so we can assume that it is a V1 file here.
// In a v1 file the specific segment could be anything, as it was randomly generated.
if data[5] > CURRENT_MAX_VERSION {
return Some("1".to_string());
}

// Since the versions are zero-indexed, we need to add one here to get the correct version.
Some((data[5] + 1).to_string())
}

/// Apply any specified codec settings.
///
/// # Arguments
Expand Down Expand Up @@ -506,6 +547,12 @@ fn show_examples() {
println!("You will be prompted for a password after executing this command.");
println!("If any data was successfully decoded then it will be displayed on screen.");
println!("{split}");
println!("psistega3 -d -v GUESS \"C:\\reference.png\" \"C:\\encoded.png\"");
println!();
println!("This command will attempt to guess the version, based on the file contents. If this fails, it will default to v1 file for legacy reasons.");
println!("You will be prompted for a password after executing this command.");
println!("If any data was successfully decoded then it will be displayed on screen.");
println!("{split}");
println!(
"psistega3 -ef -v 1 \"C:\\reference.png\" \"C:\\encoded.png\" \"C:\\input_file_path.foo\""
);
Expand Down
5 changes: 1 addition & 4 deletions psistega3-core/src/codecs/v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,7 @@ impl StegaV1 {
///
/// * `path` - The path to the PNG file.
///
pub fn process_bkgd_chunk(&mut self, path: &str) -> bool {
pub(crate) fn process_bkgd_chunk(&mut self, path: &str) -> bool {
let Some(chunk) = png_utils::read_chunk_raw(path, PngChunkType::Bkgd) else {
// This is an error as we should always have a bKGD chunk.
return false;
Expand All @@ -597,9 +597,6 @@ impl StegaV1 {
return false;
}

// NOTE: in v2 and beyond we will enforce the version here, but for this legacy
// version we will not do so.

// The 1st bit are be stored in byte 1.
// Bit 1 stores the flag indicating whether the file locker should be
// used with this file.
Expand Down
7 changes: 1 addition & 6 deletions psistega3-core/src/codecs/v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,7 @@ impl StegaV2 {
///
/// * `path` - The path to the PNG file.
///
pub fn process_bkgd_chunk(&mut self, path: &str) -> bool {
pub(crate) fn process_bkgd_chunk(&mut self, path: &str) -> bool {
let Some(chunk) = png_utils::read_chunk_raw(path, PngChunkType::Bkgd) else {
// This is an error as we should always have a bKGD chunk.
return false;
Expand All @@ -597,11 +597,6 @@ impl StegaV2 {
return false;
}

if data[5] != CODED_VERSION {
// The codec version does not match.
return false;
}

// The 1st bit are be stored in byte 1.
// Bit 1 stores the flag indicating whether the file locker should be
// used with this file.
Expand Down
6 changes: 3 additions & 3 deletions psistega3-core/src/utilities/png_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::fs::File;
use super::{file_utils, misc_utils};

#[allow(dead_code)]
pub(crate) enum PngChunkType {
pub enum PngChunkType {
Bkgd,
Idat,
}
Expand Down Expand Up @@ -96,7 +96,7 @@ pub(crate) fn generate_bkgd_chunk(data: &[u8]) -> Vec<u8> {
///
/// * `data` - The contents of the PNG chunk.
///
pub(crate) fn get_chunk_data(data: &[u8]) -> Option<&[u8]> {
pub fn get_chunk_data(data: &[u8]) -> Option<&[u8]> {
let data_len = get_chunk_length(data)?;

let start = 8;
Expand Down Expand Up @@ -168,7 +168,7 @@ pub fn insert_or_replace_bkgd_chunk(path: &str, data: &[u8]) -> Result<()> {
///
/// `Note:` this function is only designed to read the **first** instance of the chunk present within the file.
///
pub(crate) fn read_chunk_raw(path: &str, chunk_type: PngChunkType) -> Option<Vec<u8>> {
pub fn read_chunk_raw(path: &str, chunk_type: PngChunkType) -> Option<Vec<u8>> {
// If we have a chunk present then the index of
// the header will be returned.
let start = find_chunk_start(path, chunk_type)?;
Expand Down

0 comments on commit 0925bc2

Please sign in to comment.