diff options
author | Silas Bartha <[email protected]> | 2024-05-27 14:20:08 -0400 |
---|---|---|
committer | Silas Bartha <[email protected]> | 2024-05-27 14:20:08 -0400 |
commit | ccb19be9d0e918070029f31a86c7eb546121bd87 (patch) | |
tree | 6192bc514a7ae815a8863a23b4cc59405c18ec37 /src | |
parent | d088553bd19c58543b908936ba8bae2bfa877b50 (diff) |
Updated docs
Diffstat (limited to 'src')
-rw-r--r-- | src/codec.rs | 24 | ||||
-rw-r--r-- | src/jpeg/mod.rs (renamed from src/jpg/mod.rs) | 0 | ||||
-rw-r--r-- | src/jpeg/segment.rs (renamed from src/jpg/segment.rs) | 21 | ||||
-rw-r--r-- | src/lib.rs | 14 | ||||
-rw-r--r-- | src/lossless/lsb.rs (renamed from src/lossless/champleve.rs) | 35 | ||||
-rw-r--r-- | src/lossless/mod.rs | 4 |
6 files changed, 75 insertions, 23 deletions
diff --git a/src/codec.rs b/src/codec.rs index 03cdf15..8b38a9b 100644 --- a/src/codec.rs +++ b/src/codec.rs @@ -1,14 +1,26 @@ +/// Codecs enable the concealment of payload data inside the data of a carrier. pub trait Codec { + /// Data type representing the carrier. type Carrier; + + /// Data type representing the payload. type Payload; + + /// Data type representing encoder output/decoder input (usually the same as the carrier). type Output; + + /// Type of errors produced by this codec. type Error; - fn encode( - &self, - carrier: impl Into<Self::Carrier>, - payload: impl Into<Self::Payload>, - ) -> Result<Self::Output, Self::Error>; + /// Embeds payload data inside carrier, returning the result. + fn encode<C, P>(&self, carrier: C, payload: P) -> Result<Self::Output, Self::Error> + where + C: Into<Self::Carrier>, + P: Into<Self::Payload>; - fn decode(&self, encoded: impl Into<Self::Output>) -> Result<(Self::Carrier, Self::Payload), Self::Error>; + /// Extracts payload data from an encoded carrier, returning the carrier with data removed and the + /// payload data. + fn decode<E>(&self, encoded: E) -> Result<(Self::Carrier, Self::Payload), Self::Error> + where + E: Into<Self::Output>; } diff --git a/src/jpg/mod.rs b/src/jpeg/mod.rs index 3d6bdc3..3d6bdc3 100644 --- a/src/jpg/mod.rs +++ b/src/jpeg/mod.rs diff --git a/src/jpg/segment.rs b/src/jpeg/segment.rs index f54d0e2..b20ba3b 100644 --- a/src/jpg/segment.rs +++ b/src/jpeg/segment.rs @@ -5,8 +5,11 @@ use thiserror::Error; use crate::codec::Codec; +/// Codec for storing payload data in JPEG comment (COM) segments. Can store an arbitrary amount of +/// data, as long as the number of comment segments does not exceed u64::MAX. #[derive(Debug, PartialEq, Eq)] pub struct JpegSegmentCodec { + /// Index of segment to insert comments at. pub start_index: usize, } @@ -16,7 +19,11 @@ impl Codec for JpegSegmentCodec { type Output = Self::Carrier; type Error = JpegSegmentError; - fn encode(&self, carrier: impl Into<Self::Carrier>, payload: impl Into<Self::Payload>) -> Result<Self::Output, Self::Error> { + fn encode<C, P>(&self, carrier: C, payload: P) -> Result<Self::Output, Self::Error> + where + C: Into<Self::Carrier>, + P: Into<Self::Payload>, + { let mut jpeg = match Jpeg::from_bytes(carrier.into().into()) { Ok(image) => image, Err(err) => return Err(JpegSegmentError::ParseFailed { inner: err }) @@ -31,7 +38,10 @@ impl Codec for JpegSegmentCodec { Ok(jpeg.encoder().bytes().to_vec()) } - fn decode(&self, encoded: impl Into<Self::Output>) -> Result<(Self::Carrier, Self::Payload), Self::Error> { + fn decode<E>(&self, encoded: E) -> Result<(Self::Carrier, Self::Payload), Self::Error> + where + E: Into<Self::Output>, + { let mut jpeg = match Jpeg::from_bytes(encoded.into().into()) { Ok(image) => image, Err(err) => return Err(JpegSegmentError::ParseFailed { inner: err }) @@ -59,8 +69,13 @@ impl Default for JpegSegmentCodec { } } +/// Errors thrown by the JPEG segment codec. #[derive(Error, Debug)] pub enum JpegSegmentError { + /// Parsing JPEG data failed. #[error("Failed to parse JPEG data: {inner:?}")] - ParseFailed { inner: img_parts::Error } + ParseFailed { + /// Error thrown by parser. + inner: img_parts::Error, + } } @@ -1,7 +1,15 @@ -pub mod codec; +#![warn(missing_docs)] -#[cfg(feature = "jpg")] -pub mod jpg; +//! Library providing steganography codecs for various carrier and payload types, designed to be +//! extensible. +mod codec; +pub use codec::*; + +/// Codecs for carriers in JPEG format. +#[cfg(feature = "jpeg")] +pub mod jpeg; + +/// Codecs for carriers in lossless image formats (PNG, WebP, etc.). #[cfg(feature = "lossless")] pub mod lossless; diff --git a/src/lossless/champleve.rs b/src/lossless/lsb.rs index 948408c..59dcc0b 100644 --- a/src/lossless/champleve.rs +++ b/src/lossless/lsb.rs @@ -5,21 +5,29 @@ use thiserror::Error; use crate::codec::Codec; +/// Least-significant bit (LSB) steganography encodes data in the least-significant bits of colors +/// in an image. This implementation reduces the colors in the carrier (irreversibly) in order to +/// allow a byte of data to fit in each pixel of the image. 3 bits of data are encoded per pixel, +/// and the 9th bit is used to signal the end of data. #[derive(Debug)] -pub struct ChampleveCodec; +pub struct LsbCodec; -impl Codec for ChampleveCodec { +impl Codec for LsbCodec { type Carrier = DynamicImage; type Payload = Vec<u8>; type Output = Self::Carrier; - type Error = ChampleveError; + type Error = LsbError; - fn encode(&self, carrier: impl Into<Self::Carrier>, payload: impl Into<Self::Payload>) -> Result<Self::Output, Self::Error> { + fn encode<C, P>(&self, carrier: C, payload: P) -> Result<Self::Output, Self::Error> + where + C: Into<Self::Carrier>, + P: Into<Self::Payload>, + { let mut image: DynamicImage = carrier.into(); let payload: Vec<u8> = payload.into(); if image.pixels().count() < payload.len() { - return Err(ChampleveError::PayloadTooBig); + return Err(LsbError::PayloadTooBig); } let mut payload_iter = payload.iter(); @@ -43,13 +51,16 @@ impl Codec for ChampleveCodec { } } }, - _ => return Err(ChampleveError::UnsupportedFormat { format: image.color() }) + _ => return Err(LsbError::UnsupportedFormat { format: image.color() }) } Ok(image) } - fn decode(&self, carrier: impl Into<Self::Output>) -> Result<(Self::Carrier, Self::Payload), ChampleveError> { + fn decode<E>(&self, carrier: E) -> Result<(Self::Carrier, Self::Payload), LsbError> + where + E: Into<Self::Output>, + { let mut image: DynamicImage = carrier.into(); let mut payload: Vec<u8> = Vec::new(); @@ -72,7 +83,7 @@ impl Codec for ChampleveCodec { } } }, - _ => return Err(ChampleveError::UnsupportedFormat { format: image.color() }) + _ => return Err(LsbError::UnsupportedFormat { format: image.color() }) } Ok((image, payload)) @@ -128,12 +139,18 @@ fn decode_pixel<P: Pixel<Subpixel = u8>>(pixel: &mut P) -> Option<u8> { Some(payload_byte) } +/// Errors thrown by the LSB Codec. #[derive(Error, Debug)] -pub enum ChampleveError { +pub enum LsbError { + + /// Error thrown when payload is too big for the carrier. #[error("Payload is too big for the carrier. Choose a smaller payload or an image with greater pixel dimensions.")] PayloadTooBig, + + /// Error thrown when pixel format is unsupported. #[error("Specified image format ({format:?}) is unsupported.")] UnsupportedFormat { + /// Provided (invalid) format. format: ColorType }, } diff --git a/src/lossless/mod.rs b/src/lossless/mod.rs index a9ae225..a6bda54 100644 --- a/src/lossless/mod.rs +++ b/src/lossless/mod.rs @@ -1,2 +1,2 @@ -mod champleve; -pub use champleve::*; +mod lsb; +pub use lsb::*; |