Skip to content

Commit

Permalink
feat!: #7 error handling in stegano-core
Browse files Browse the repository at this point in the history
- make the message struct creation fallible
- apply the necessary fallible code all the layers up
- add an old demo image format for tests
  • Loading branch information
sassman committed Oct 6, 2023
1 parent 2320232 commit e04b75c
Show file tree
Hide file tree
Showing 13 changed files with 304 additions and 227 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion stegano-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ fn main() -> Result<()> {
}

if let Some(files) = m.get_many::<String>("data_file") {
s.hide_files(files.map(|f| &**f).collect());
s.hide_files(files.map(|f| &**f).collect())?;
}

s.hide();
Expand Down
4 changes: 2 additions & 2 deletions stegano-core/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub fn unveil(
let files = match media {
Media::Image(image) => {
let mut decoder = LsbCodec::decoder(&image, opts);
let msg = Message::of(&mut decoder, PayloadCodecFactory::default());
let msg = Message::of(&mut decoder, PayloadCodecFactory).unwrap();
let mut files = msg.files;

if let Some(text) = msg.text {
Expand All @@ -29,7 +29,7 @@ pub fn unveil(
Media::Audio(audio) => {
let mut decoder = Decoder::new(AudioWavIter::new(audio.1.into_iter()), OneBitUnveil);

let msg = Message::of(&mut decoder, PayloadCodecFactory::default());
let msg = Message::of(&mut decoder, PayloadCodecFactory).unwrap();
let mut files = msg.files;

if let Some(text) = msg.text {
Expand Down
62 changes: 62 additions & 0 deletions stegano-core/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use std::string::FromUtf8Error;
use thiserror::Error;
use zip::result::ZipError;

#[derive(Error, Debug)]
pub enum SteganoError {
/// Represents an unsupported carrier media. For example, a Movie file is not supported
#[error("Media format is not supported")]
UnsupportedMedia,

/// Represents an invalid carrier audio media. For example, a broken WAV file
#[error("Audio media is invalid")]
InvalidAudioMedia,

/// Represents an invalid carrier image media. For example, a broken PNG file
#[error("Image media is invalid")]
InvalidImageMedia,

/// Represents an unsupported message format version, for example foreign formats or just data crap
#[error("Unsupported message format version: {0}")]
UnsupportedMessageFormat(u8),

/// Represents the error of invalid UTF-8 text data found inside of a text only message
#[error("Invalid text data found inside a message")]
InvalidTextData(#[from] FromUtf8Error),

/// Represents an unveil of no secret data. For example when a media did not contain any secrets
#[error("No secret data found")]
NoSecretData,

/// Represents an error caused by an invalid filename, for example not unsupported charset or empty filename
#[error("A file with an invalid file name was provided")]
InvalidFileName,

/// Represents an error when interacting with the document message payload
#[error("Error during the payload processing for documents")]
PayloadProcessingError(#[from] ZipError),

/// Represents a failure to read from input.
#[error("Read error")]
ReadError { source: std::io::Error },

/// Represents a failure to write target file.
#[error("Write error")]
WriteError { source: std::io::Error },

/// Represents a failure when encoding an audio file.
#[error("Audio encoding error")]
AudioEncodingError,

/// Represents a failure when encoding an image file.
#[error("Image encoding error")]
ImageEncodingError,

/// Represents a failure when creating an audio file.
#[error("Audio creation error")]
AudioCreationError,

/// Represents all other cases of `std::io::Error`.
#[error(transparent)]
IoError(#[from] std::io::Error),
}
92 changes: 27 additions & 65 deletions stegano-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
//! use stegano_core::{SteganoCore, SteganoEncoder};
//!
//! SteganoCore::encoder()
//! .hide_file("Cargo.toml")
//! .hide_file("Cargo.toml").unwrap()
//! .use_media("../resources/plain/carrier-image.png").unwrap()
//! .write_to("image-with-a-file-inside.png")
//! .hide();
Expand All @@ -27,15 +27,15 @@
//! use std::path::Path;
//!
//! SteganoCore::encoder()
//! .hide_file("Cargo.toml")
//! .hide_file("Cargo.toml").unwrap()
//! .use_media("../resources/plain/carrier-image.png").unwrap()
//! .write_to("image-with-a-file-inside.png")
//! .hide();
//!
//! unveil(
//! &Path::new("image-with-a-file-inside.png"),
//! &Path::new("./"),
//! &CodecOptions::default());
//! &CodecOptions::default()).unwrap();
//! ```
//!
//! [core]: ./struct.SteganoCore.html
Expand All @@ -44,6 +44,7 @@
//! [raw]: ./struct.SteganoRawDecoder.html

#![warn(
// clippy::unwrap_used,
// clippy::cargo_common_metadata,
// clippy::branches_sharing_code,
// clippy::cast_lossless,
Expand Down Expand Up @@ -78,8 +79,11 @@ pub mod raw_message;

pub use raw_message::*;

pub mod api;
pub mod commands;
pub mod error;
pub mod media;
pub mod result;
pub mod universal_decoder;
pub mod universal_encoder;

Expand All @@ -88,53 +92,10 @@ use image::RgbaImage;
use std::default::Default;
use std::fs::File;
use std::path::Path;
use thiserror::Error;

pub use crate::error::SteganoError;
pub use crate::media::image::CodecOptions;
use crate::media::payload::PayloadCodecFeatures;

#[derive(Error, Debug)]
pub enum SteganoError {
/// Represents an unsupported carrier media. For example, a Movie file is not supported
#[error("Media format is not supported")]
UnsupportedMedia,

/// Represents an invalid carrier audio media. For example, a broken WAV file
#[error("Audio media is invalid")]
InvalidAudioMedia,

/// Represents an invalid carrier image media. For example, a broken PNG file
#[error("Image media is invalid")]
InvalidImageMedia,

/// Represents an unveil of no secret data. For example when a media did not contain any secrets
#[error("No secret data found")]
NoSecretData,

/// Represents a failure to read from input.
#[error("Read error")]
ReadError { source: std::io::Error },

/// Represents a failure to write target file.
#[error("Write error")]
WriteError { source: std::io::Error },

/// Represents a failure when encoding an audio file.
#[error("Audio encoding error")]
AudioEncodingError,

/// Represents a failure when encoding an image file.
#[error("Image encoding error")]
ImageEncodingError,

/// Represents a failure when creating an audio file.
#[error("Audio creation error")]
AudioCreationError,

/// Represents all other cases of `std::io::Error`.
#[error(transparent)]
IoError(#[from] std::io::Error),
}
pub use crate::result::Result;

/// wrap the low level data types that carries information
#[derive(Debug, Eq, PartialEq)]
Expand Down Expand Up @@ -171,7 +132,6 @@ impl HideBit for MediaPrimitiveMut<'_> {
}

pub type WavAudio = (WavSpec, Vec<i16>);
pub type Result<E> = std::result::Result<E, SteganoError>;

/// a media container for steganography
pub enum Media {
Expand Down Expand Up @@ -266,7 +226,7 @@ impl Hide for Media {
message: &Message,
opts: &CodecOptions,
) -> Result<&mut Media> {
let buf: Vec<u8> = message.into();
let buf: Vec<u8> = message.try_into()?;

match self {
Media::Image(i) => {
Expand Down Expand Up @@ -339,22 +299,22 @@ impl SteganoEncoder {
self
}

pub fn hide_file(&mut self, input_file: &str) -> &mut Self {
pub fn hide_file(&mut self, input_file: &str) -> Result<&mut Self> {
{
let _f = File::open(input_file).expect("Data file was not readable.");
}
self.message.add_file(input_file);
self.message.add_file(input_file)?;

self
Ok(self)
}

pub fn hide_files(&mut self, input_files: Vec<&str>) -> &mut Self {
pub fn hide_files(&mut self, input_files: Vec<&str>) -> Result<&mut Self> {
self.message.files = Vec::new();
input_files.iter().for_each(|&f| {
self.hide_file(f);
});
for f in input_files.iter() {
self.hide_file(f)?;
}

self
Ok(self)
}

pub fn hide(&mut self) -> &Self {
Expand Down Expand Up @@ -396,13 +356,15 @@ mod e2e_tests {
#[test]
#[should_panic(expected = "Data file was not readable.")]
fn should_panic_on_invalid_data_file() {
SteganoEncoder::new().hide_file("foofile");
SteganoEncoder::new().hide_file("foofile").unwrap();
}

#[test]
#[should_panic(expected = "Data file was not readable.")]
fn should_panic_on_invalid_data_file_among_valid() {
SteganoEncoder::new().hide_files(vec!["Cargo.toml", "foofile"]);
SteganoEncoder::new()
.hide_files(vec!["Cargo.toml", "foofile"])
.unwrap();
}

#[test]
Expand Down Expand Up @@ -449,7 +411,7 @@ mod e2e_tests {
let secret_media_f = secret_media_p.to_str().unwrap();

SteganoEncoder::new()
.hide_file("Cargo.toml")
.hide_file("Cargo.toml")?
.use_media("../resources/plain/carrier-audio.wav")?
.write_to(secret_media_f)
.hide();
Expand Down Expand Up @@ -482,7 +444,7 @@ mod e2e_tests {
let image_with_secret = image_with_secret_path.to_str().unwrap();

SteganoEncoder::new()
.hide_file("Cargo.toml")
.hide_file("Cargo.toml")?
.use_media("../resources/with_text/hello_world.png")?
.write_to(image_with_secret)
.hide();
Expand Down Expand Up @@ -538,7 +500,7 @@ mod e2e_tests {
let expected_file = out_dir.path().join("random_1666_byte.bin");

SteganoEncoder::new()
.hide_file(secret_to_hide)
.hide_file(secret_to_hide)?
.use_media(BASE_IMAGE)?
.write_to(image_with_secret)
.hide();
Expand Down Expand Up @@ -571,7 +533,7 @@ mod e2e_tests {
let expected_file = out_dir.path().join("zip_with_2_files.zip");

SteganoEncoder::new()
.hide_file(secret_to_hide)
.hide_file(secret_to_hide)?
.use_media(BASE_IMAGE)?
.write_to(image_with_secret)
.hide();
Expand Down Expand Up @@ -650,7 +612,7 @@ mod e2e_tests {
SteganoEncoder::new()
// .force_content_version(PayloadCodecFeatures::V2)
.use_media(BASE_IMAGE)?
.hide_file(secret_to_hide)
.hide_file(secret_to_hide)?
.write_to(image_with_secret)
.hide();

Expand Down
12 changes: 9 additions & 3 deletions stegano-core/src/media/image/lsb_codec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,22 @@ use crate::media::image::decoder::ImageRgbaColor;
use crate::media::image::encoder::ImageRgbaColorMut;
use crate::universal_decoder::{Decoder, OneBitUnveil};
use crate::universal_encoder::{Encoder, HideAlgorithms, OneBitHide, OneBitInLowFrequencyHide};

use image::RgbaImage;
use std::io::{Read, Write};

#[derive(Debug)]
pub struct CodecOptions {
/// would move the by step n each iteration,
/// would move the by step n each iteration, this reduces the capacity available.
///
/// Note: the alpha channel is count as regular channel
pub color_channel_step_increment: usize,
/// if true no alpha channel would be used for encoding

/// If true no alpha channel would be used for encoding,
/// this reduces then the capacity by one bit per pixel
pub skip_alpha_channel: bool,
/// the concealer strategy

/// the concealer strategy, decides on where in a color channel things are going to be hidden
pub concealer: Concealer,
}

Expand All @@ -23,6 +28,7 @@ pub enum Concealer {
}

impl Default for CodecOptions {
/// The good old golden options
fn default() -> Self {
Self {
color_channel_step_increment: 1,
Expand Down
Loading

0 comments on commit e04b75c

Please sign in to comment.