Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #235 +/- ##
==========================================
+ Coverage 82.44% 86.38% +3.93%
==========================================
Files 29 38 +9
Lines 1954 5023 +3069
Branches 1954 0 -1954
==========================================
+ Hits 1611 4339 +2728
- Misses 226 684 +458
+ Partials 117 0 -117
🚀 New features to boost your workflow:
|
sassman
commented
Jan 26, 2026
- stack overflow silently truncated the 70% quality JPEG - unify to single heap-based `prepare_chess_image` generator - extract `prepare_fixture_image` helper to reduce duplication Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
- matrix encoding with check matrix H_w for (1, n, k) scheme - permutative straddling via Fisher-Yates shuffle (fastrand) - encoder/decoder with shrinkage handling on synthetic DCT coefficients - roundtrip tests pass for various message sizes Phase 2 (JPEG codec integration) not yet started - requires forked jpeg-decoder/encoder crates to access DCT coefficients. Includes architecture doc and research paper for reference. Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
- add coefficient-level JPEG transcode pipeline (parse → huffman decode → F5 → huffman encode → write) - preserve image quality by operating on quantized DCT coefficients without requantization - split scan module into baseline.rs for future progressive JPEG support Key components: - huffman.rs: BitReader/BitWriter with byte stuffing, Huffman encode/decode - parser.rs: JPEG segment parsing, quantization and Huffman tables - scan/baseline.rs: baseline JPEG coefficient encode/decode - writer.rs: JPEG reassembly with modified scan data Includes: - hide/unveil CLI examples for quick testing - architecture docs with progressive JPEG extension plan - 73 tests covering roundtrip encode/decode and F5 embedding
…bmodules - custom huffman/marker/scan parser was incomplete and hard to extend; forked jpeg-encoder and jpeg-decoder provide full, tested JPEG codecs with surgical hooks for F5 coefficient access - encoder fork: `set_coefficient_hook()` intercepts after DCT+quantization - decoder fork: `decode_raw_coefficients()` returns quantized DCT coefficients - `jpeg_ops.rs` orchestrates both forks, normalizing coefficient ordering (encoder=zigzag, decoder=natural) for consistent F5 embedding/extraction - stegano-inspect `quantization` subcommand rewired to use decoder fork - stegano-inspect `summary` subcommand added: dimensions, coding process, baseline flag, color model, sampling factors, metadata presence, F5 capacity - adds original F5 paper (Westfeld) as research reference Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Adds execute_jpeg() method that: - Serializes Message to bytes with optional encryption via FabS - Uses password as F5 permutation seed for shuffled DCT access - Supports JPEG→JPEG transcoding and PNG→JPEG conversion
Transparent JPEG support in execute(): - Detects JPEG input by file extension internally - Extracts F5 data using password as permutation seed - Wrong password fails both coefficient order and decryption
- Fix typo in workspace Cargo.toml (versoin -> version) - Apply rustfmt formatting to stegano-core API files - Move test module to end of file in types.rs - Replace manual clamp/contains patterns with idiomatic methods - Replace range loops with iterator patterns - Remove useless vec! when array suffices - Replace assert_eq! with bool literals with assert!/assert! - Update submodules with clippy fixes
The F5 JPEG steganography feature uses git submodules for the modified JPEG encoder and decoder crates. This updates the CI workflow to initialize submodules recursively during checkout. Previously the workflow used reusable workflows that didn't support submodules, so the jobs are now inlined with the submodules: recursive option added to each checkout step.
…nested errors - Remove string fields from error variants for cleaner API - Wrap JPEG codec errors in Box<dyn Error> to preserve error chain - Custom Debug impl delegates to Display for user-friendly unwrap() - Add e2e tests verifying error messages and source chain integrity
Eliminates JPEG code duplication by moving F5 logic into `Media.hide_data()`: - Codec choice now determines output format: `CodecOptions::Lsb` → PNG, `F5` → JPEG, `AudioLsb` → WAV - `Media::ImageJpeg` variant stores source bytes for JPEG→JPEG transcoding - `EncodedMedia` enum carries encoded data ready for saving - `SteganoEncoder` derives codec from target path extension - `HideApi.execute_jpeg()` removed; unified path via `SteganoEncoder` - `PayloadCodecFactory.password()` method added for F5 seed derivation Step 2 will apply same pattern to decoding/unveil. Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
- Remove is_jpeg_extension() check in execute() - Rename unveil_standard() to unveil() - ImageJpeg now uses F5JpegDecoder instead of LSB - Delete duplicate unveil_jpeg() method
- Export LsbCodecOptions from stegano-core - Update main.rs to return LsbCodecOptions from get_options() - Update hide.rs to remove with_options() (codec now determined by target extension) - Update unveil.rs and unveil_raw.rs to use LsbCodecOptions
Instead of exposing LsbCodecOptions to CLI consumers, provide specific builder methods for each configurable parameter: - Add with_color_step_increment() to UnveilApi and UnveilRawApi - Remove with_options() that took LsbCodecOptions - CLI now passes individual values, not codec option structs - Core library internally decides which codec to use based on media type This keeps codec implementation details internal while still allowing configuration of relevant parameters.
- Add color_channel_step_increment field to SteganoEncoder - Add with_color_step_increment() to SteganoEncoder and HideApi - Use the value when building LsbCodecOptions for PNG output - CLI now properly passes the color step increment for hide operations
- make encoder coefficient hook fallible (returns Result) - add EmbeddingFailed variant to F5Error for hook errors - F5 capacity exceeded now fails at hide time, not unveil time - rename F5JpegDecoder::new() to decode() for clarity Previously, embedding errors (e.g. message too large) were silently swallowed in the hook, only surfacing later during extraction. Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
…ding Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
- add DEFAULT_JPEG_QUALITY const (90) to codec_options - add jpeg_quality field and with_jpeg_quality() to SteganoEncoder - add jpeg_quality field and with_jpeg_quality() to HideApi - add --jpeg-quality CLI arg with 1-100 range validation - add integration test for custom quality roundtrip Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Allows consumers to predict encoded payload size before encoding, enabling accurate capacity checking against carrier limits. - Add encoded_size() method to PayloadEncoder trait - Implement for PayloadEncoderWithLengthHeader (content + 6 bytes overhead) - Implement for PayloadFlexCodec (delegates to inner encoder) - Implement for CryptedPayloadCodec (encryption overhead + inner encoding) - Add test verifying predicted size matches actual encoding
Exposes F5 header overhead (4 bytes) so consumers can accurately calculate total capacity requirements for embedding.
The test now correctly uses F5Encoder::HEADER_BYTES to calculate usable capacity, ensuring the right-sized message actually fits.
- Add #[allow(dead_code)] to encoded_size trait method (API for future use) - Add #[allow(dead_code)] to ENCRYPTION_OVERHEAD constant (used internally) - Apply cargo fmt formatting to test assertions
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
Signed-off-by: Sven Kanoldt <sven@d34dl0ck.me>
00b965c to
b1e3d60
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds JPEG steganography support using the F5 algorithm, enabling users to hide and extract secret data in JPEG images alongside the existing PNG and WAV support.
What is F5?
F5 is a steganographic algorithm designed specifically for JPEG images. Unlike naive LSB embedding in spatial domain:
This makes F5 more statistically secure than naive approaches — changes to the coefficient histogram are minimal and spread across the image.
Usage Examples
CLI
Rust API
Cross-format (PNG → JPEG)
Security Model
Test Plan