Embed and extract files hidden inside PNG images using N-bit LSB steganography.
Project description
LSB Tool
Embed files inside a PNG image or extract previously hidden files using N-bit LSB steganography secured by a password.
Files are scattered across pixels in a password-derived pseudo-random order so the hidden data is not readable by standard steganalysis tools that scan sequential pixel runs.
Features
| All image modes | 1-bit, 8-bit grayscale/palette, 16-bit grayscale, RGB, RGBA |
| N-bit embedding | Store 1–N bits per channel (N limited by channel bit-depth) |
| Multiple files | Embed any number of files in a single carrier image |
| Filename storage | Optionally preserve original filenames on extraction |
| Error codes | Every failure exits with a distinct code — see ERRORS.md |
Requirements
- Python 3.8+
- Pillow ≥ 8.0
- NumPy ≥ 1.20
pip install -r requirements.txt
Usage
Embed
python main.py -E -i carrier.png -p password -f file1.txt file2.bin
Options:
| Flag | Description |
|---|---|
-i |
Carrier image (any PIL-supported format; output is always PNG) |
-p |
Password used to derive the pixel-shuffle seed and verification hash |
-f |
One or more files to embed |
-l |
Bits per channel to use — embedding depth (default 1, max depends on image type) |
-n |
Filename field length in bytes 0–255 (default 0 = filenames not stored) |
-v |
Print capacity and hash diagnostics after the operation |
Output: <original_name>_embedded.png in the current directory.
Extract
python main.py -e -i carrier_embedded.png -p password
Extracted files are written to the current directory. If -n was used during
embedding, the original filenames are restored; otherwise files are named
extracted_file_0, extracted_file_1, …
Embedding depth (-l)
Higher depth stores more bits per channel, multiplying capacity roughly proportionally, but making changes more visible to the human eye.
| Image type | Max depth | Capacity formula (bytes) |
|---|---|---|
1-bit (1) |
1 | (pixels − 5) × 1 / 8 |
8-bit (L, LA, RGB, RGBA, P) |
8 | (pixels − preamble) × channels × depth / 8 |
16-bit grayscale (I;16) |
16 | same formula |
If you request a depth greater than the image supports, the tool warns and clamps to the maximum automatically.
Examples
# Embed two files at depth 1 (default)
python main.py -E -i photo.png -p hunter2 -f report.pdf archive.zip
# Extract (restores original files)
python main.py -e -i photo_embedded.png -p hunter2
# Embed with filename storage and depth 4
python main.py -E -i photo.png -p hunter2 -f secret.txt -l 4 -n 32
# Extract — file is written as "secret.txt" instead of "extracted_file_0"
python main.py -e -i photo_embedded.png -p hunter2
# Check capacity (verbose)
python main.py -E -i photo.png -p hunter2 -f data.bin -v
Image format note
Always use PNG for the carrier image. Lossy formats (JPEG, WebP) alter pixel values after saving and will destroy the hidden data. The tool always saves output as PNG regardless of the input format.
Running tests
pip install pytest
pytest tests/ -v
Error codes
All errors exit with a specific code and print [Exx] … to stderr.
See ERRORS.md for the full reference.
| Code | Meaning |
|---|---|
| E02 | No mode selected (-E or -e required) |
| E03 | Carrier image not found |
| E04 | A file to embed was not found |
| E05 | Files too large for this image at the chosen depth |
| E06 | Wrong password or no embedded data |
| E07 | Image file is corrupt or unsupported |
| E08 | Cannot write an output file |
| E10 | Internal error (bug) |
How it works
- Key derivation — The password and image dimensions are hashed (SHA-256,
iterated
width × heighttimes) to produce a 64-character hex digest. - Pixel shuffle — A NumPy PCG64 RNG seeded from the digest shuffles all pixel coordinates into a pseudo-random order.
- Preamble — The first
⌈13/channels⌉shuffled pixels store a 13-bit header at LSB-only depth: 5 bits for the embedding depth, 8 bits for the filename-field length. - Main data — Remaining shuffled pixels carry the payload at the chosen
depth using the formula
bit k → pixel = k ÷ (channels × depth),channel = (k ÷ depth) mod channels,bit_pos = k mod depth. - Verification — The first 512 bits of the main region store the password-derived SHA-256 hash as ASCII. Extraction fails with E06 if it does not match.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file lsb_tool-2.0.0-py3-none-any.whl.
File metadata
- Download URL: lsb_tool-2.0.0-py3-none-any.whl
- Upload date:
- Size: 14.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d949144bc7554d6f9d72fb911d8581d3c27a3d1c2d2e9c308f45bce851c87ffc
|
|
| MD5 |
34af09e15d6608a992821da65a05b9e0
|
|
| BLAKE2b-256 |
e61ac024fe94fecfc994a54f74a7b3d27bfb890b6f983fd4ed41d36c1fe17d25
|
Provenance
The following attestation bundles were made for lsb_tool-2.0.0-py3-none-any.whl:
Publisher:
publish.yml on Rkoed97/LSB-tool
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
lsb_tool-2.0.0-py3-none-any.whl -
Subject digest:
d949144bc7554d6f9d72fb911d8581d3c27a3d1c2d2e9c308f45bce851c87ffc - Sigstore transparency entry: 1108155460
- Sigstore integration time:
-
Permalink:
Rkoed97/LSB-tool@40ac0dba0e70331d4196a2494702541e430d1095 -
Branch / Tag:
refs/tags/v2.0.0 - Owner: https://github.com/Rkoed97
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@40ac0dba0e70331d4196a2494702541e430d1095 -
Trigger Event:
push
-
Statement type: