# SteganoN
**Steganon** is an extended implementation of the [LSB Matching steganography algorithm](https://www.google.com/search?q=LSB+Matching+steganography+algorithm).
In short, steganography **LSB** is a method of hiding data inside pixels of image. Every pixel of a regular image typically consist of three integers (**0-255**, or **one byte**). They describe an amount of **R**ed, **G**reen and **B**lue colors. We take User's data to hide and convert it to a **bits**; then we take the next target pixel and select first (color channel) integer: **R**. If the last bit of color channel is the **same as our next bit** from the data bit string, **we do nothing**; **if they differ**, we **randomly add or subtract 1 from color channel**. For example, if channel is `222`, we **randomly** `+1` or `-1`, so result would be either `221` or `223`. However, **if channel is** `0` **we always** `+1`, and **if** `255` **we always** `-1`. This essentially will change the [least significant bit](https://en.wikipedia.org/wiki/Bit_numbering) of color channel (e.g **R**) to the bit we want. Repeat this process for **G** and **B** then write altered pixel back into image. Repeat on the next pixels until User's data is not empty. Such change to **RGB** is invisible to human eye and gives us chance to hide **three bits** of data **per pixel** (or, in this library, — three pixels per byte).
## Difference between classic LSB and LSB\_MWS
This repository implements a special type of LSB Matching: **LSB Matching With [Seed](https://en.wikipedia.org/wiki/Random_seed)** (short. `LSB_MWS`). This algorithm utilizes [**PRNG**](https://en.wikipedia.org/wiki/Pseudorandom_number_generator) with changeable [seed](https://en.wikipedia.org/wiki/Random_seed) to select targeted pixels. Moreover, in `LSB_MWS`, one cover Image may contain different, multiple secret Datas on different, multiple Seeds. Thus, this project supports the **Deniable Hide** feature — you can reveal as much Data as you want.
**Deniable Hide** works as a chain of Seeds. Instead of specifying only one Seed, you can pass as many as you wish and attach unique secret Data to each one of them independently. `LSB_MWS` *Hide function* will ensure that each bit of different Datas are stored in unique Pixel without overlapping. On *Extract*, to get a first hidden Data you will need to provide a Seed`(1)`, to get a second Data — Seed`(1)` & Seed`(2)`, to get a third Data — Seed`(1)`, Seed`(2)` & Seed`(3)`, and so on. There is a **zero correlations between Seeds**. In case of extortion, **you can reveal only Seed**`(1)` and criminal—or *whoever*, will **never** know that there is more hidden data deeper.
**We don't feed Seeds directly into the PRNG**, we **hash** them firstly in a special\
manner with `SHA512` **truncated to the *last* 32 bytes**.
***(Iterations on MultiSeed with three Seeds)***
1. We create *Initializator* hash by hashing a constant-*Basis* with `ImageSize` (*Width*/*Height*);
2. We hash *Initializator* with **Seed**`(1)` — that is our ***first*** **prngS**`(1)` (PRNG Seed);
3. We hash **prngS**`(1)` with **Seed**`(2)`, — that is our ***second*** PRNG Seed;
4. We hash **prngS**`(2)` with **Seed**`(3)`, — that is our ***third*** PRNG Seed.
It means that **Seeds are dependent on each other** and *Initializator* **depends on the Image Width & Height**, thus, each unique-sized Image will utilize **different Seed values for PRNG**. This will add some protection against the brute-force or seed re-usage.
###### This can be disabled with `use_raw_seed=True` on `LSB_MWS`, though *not* recommended.
## Installation
**You can install SteganoN with PIP**
```bash
pip install steganon
```
**Or you can install it after `git clone`**
```bash
python -m venv steganon-env
cd steganon-env && . bin/activate
git clone https://github.com/NonProjects/steganon
pip install ./steganon
```
**SteganoN has basic CLI implementation**
```bash
# Install SteganoN with CLI
pip install steganon[cli]
```

## Example
### (One Seed) Hiding Secret Data
```python3
from steganon import Image, LSB_MWS
image = Image.open('example.png') # Open targeted Image
lsb_mws = LSB_MWS(image, b'seed_0') # Init with seed=b'seed_0'
lsb_mws.hide(b'Secret!!!') # Write secret message to image pixels
image.save('example.png') # Save altered image with secret data
```
### (One Seed) Extracting Secret Data
```python
from steganon import Image, LSB_MWS
image = Image.open('example.png') # Open Image with hidden data
lsb_mws = LSB_MWS(image, b'seed_0') # Init with seed=b'seed_0'
print(lsb_mws.extract()) # b'Secret!!!'
```
### (MultiSeed) Hiding Secret Data
```python3
from steganon import Image, LSB_MWS
image = Image.open('example.png') # Open targeted Image
seeds = (b'seed_0', b'seed_1', b'seed_2') # You can use as many as you want
lsb_mws = LSB_MWS(image, seeds) # Init LSB_MWS with multiple seeds
lsb_mws.hide(b'Secret data on Seed(0)!!!') # Write secret message to image pixels
lsb_mws.next() # Switch to the next Seed (b'seed_1')
lsb_mws.hide(b'Secret data on Seed(1)!!!') # Write secret message to image pixels
lsb_mws.next() # Switch to the next Seed (b'seed_2')
lsb_mws.hide(b'Secret data on Seed(2)!!!') # Write secret message to image pixels
image.save('example.png') # Save altered image with secret data
```
### (MultiSeed) Extracting Secret Data
```python
from steganon import Image, LSB_MWS
image = Image.open('example.png') # Open targeted Image
seeds = (b'seed_0', b'seed_1', b'seed_2') # You can use as many as you want
lsb_mws = LSB_MWS(image, seeds) # Init LSB_MWS with multiple seeds
print(lsb_mws.extract()) # b'Secret data on Seed(0)!!!'
lsb_mws.next() # Switch to the next Seed (b'seed_1')
print(lsb_mws.extract()) # b'Secret data on Seed(1)!!!'
lsb_mws.next() # Switch to the next Seed (b'seed_2')
print(lsb_mws.extract()) # b'Secret data on Seed(2)!!!'
```
## Image Comparison
Here is comparison between [**Original image (1)**](https://github.com/user-attachments/assets/2d639687-718e-4868-afbb-fc431b35e747) and [**Image with written on pixels data (2)**](https://github.com/user-attachments/assets/4b3de992-2fcb-4c54-84e2-94546cd0c168).
<img src="https://github.com/user-attachments/assets/b218038d-94c6-41bc-b471-9567112e6c6e" width="777" height="-1"></img>
[**Modified Image (2)**](https://github.com/user-attachments/assets/4b3de992-2fcb-4c54-84e2-94546cd0c168) has whole [**Zen of Python**](https://peps.python.org/pep-0020/#the-zen-of-python) written on it.\
You can extract Zen from **(2)** by using Seed `b'spam_eggs'`
## TestMode on LSB\_MWS
`steganon.LSB_MWS` class has a `testmode` key. We can use it to check affected pixels under different seeds
<img src="https://github.com/user-attachments/assets/a2e1beda-f205-432e-8c2e-56dfb0d8ead7" width="777" height="-1"></img>
## Additional Information
**Tested formats:**
* ✅ **PNG**
* ✅ **BMP**
* ✅ **TIFF**
* ✅ **WEBP**
* ✅ **JPEG2000**
* ❌ **JPG**
* ❌ **HEIF**
0. This library & implementation **wasn't** verified by steganography experts. **Use with caution!**
1. **Always use a different seed!** Pixel positions `*`may be the same on different Images and Data!
2. This library **will not** work with JPEG due to its lossy design. **Use lossless formats** (e.g PNG, WEBP, etc);
3. Best `**`template to hide data is a **compressed JPEG turned to PNG**. Library has `tools.pngify`, use it;
4. The **bigger your Image**, the **bigger amount of Data you can hide** (though the less data—the `***`better).
`* ` If Image Width/Height and Seed are the same. **Always use a unique Seed**\
`** ` Your cover Image **should** have a decent amount of "Noise" / Compression\
`***` Quite obviously, **the lower coverage**, the **less chance for analyze tools**
Contact me on **thenonproton@pm.me** (or just [**open Issue**](https://github.com/NonProjects/steganon/issues)) if you have any feedback on this library.
## All Aboard!
Try to download [**this example image**](https://github.com/user-attachments/assets/cd1e1785-fead-4a80-83c7-f3400334c756) and extract secret information from it with seed `b'OZZY'`\
Save data to the file with `.ogg` extension and play it with your favourite media player.
###### *Flying high again!*
Raw data
{
"_id": null,
"home_page": "https://github.com/NonProjects/steganon",
"name": "steganon",
"maintainer": null,
"docs_url": null,
"requires_python": null,
"maintainer_email": null,
"keywords": "Steganography, LSB, Cryptography",
"author": "NonProjects",
"author_email": "thenonproton@pm.me",
"download_url": "https://files.pythonhosted.org/packages/bb/ce/db7e48dfbc953f98f0c0033ef18ce2ffc85f3e18cc4f5e872f2b001c2f1c/steganon-1.1.tar.gz",
"platform": null,
"description": "# SteganoN\n\n**Steganon** is an extended implementation of the [LSB Matching steganography algorithm](https://www.google.com/search?q=LSB+Matching+steganography+algorithm).\n\nIn short, steganography **LSB** is a method of hiding data inside pixels of image. Every pixel of a regular image typically consist of three integers (**0-255**, or **one byte**). They describe an amount of **R**ed, **G**reen and **B**lue colors. We take User's data to hide and convert it to a **bits**; then we take the next target pixel and select first (color channel) integer: **R**. If the last bit of color channel is the **same as our next bit** from the data bit string, **we do nothing**; **if they differ**, we **randomly add or subtract 1 from color channel**. For example, if channel is `222`, we **randomly** `+1` or `-1`, so result would be either `221` or `223`. However, **if channel is** `0` **we always** `+1`, and **if** `255` **we always** `-1`. This essentially will change the [least significant bit](https://en.wikipedia.org/wiki/Bit_numbering) of color channel (e.g **R**) to the bit we want. Repeat this process for **G** and **B** then write altered pixel back into image. Repeat on the next pixels until User's data is not empty. Such change to **RGB** is invisible to human eye and gives us chance to hide **three bits** of data **per pixel** (or, in this library, \u2014 three pixels per byte).\n\n## Difference between classic LSB and LSB\\_MWS\n\nThis repository implements a special type of LSB Matching: **LSB Matching With [Seed](https://en.wikipedia.org/wiki/Random_seed)** (short. `LSB_MWS`). This algorithm utilizes [**PRNG**](https://en.wikipedia.org/wiki/Pseudorandom_number_generator) with changeable [seed](https://en.wikipedia.org/wiki/Random_seed) to select targeted pixels. Moreover, in `LSB_MWS`, one cover Image may contain different, multiple secret Datas on different, multiple Seeds. Thus, this project supports the **Deniable Hide** feature \u2014 you can reveal as much Data as you want.\n\n**Deniable Hide** works as a chain of Seeds. Instead of specifying only one Seed, you can pass as many as you wish and attach unique secret Data to each one of them independently. `LSB_MWS` *Hide function* will ensure that each bit of different Datas are stored in unique Pixel without overlapping. On *Extract*, to get a first hidden Data you will need to provide a Seed`(1)`, to get a second Data \u2014 Seed`(1)` & Seed`(2)`, to get a third Data \u2014 Seed`(1)`, Seed`(2)` & Seed`(3)`, and so on. There is a **zero correlations between Seeds**. In case of extortion, **you can reveal only Seed**`(1)` and criminal\u2014or *whoever*, will **never** know that there is more hidden data deeper.\n\n**We don't feed Seeds directly into the PRNG**, we **hash** them firstly in a special\\\nmanner with `SHA512` **truncated to the *last* 32 bytes**.\n\n***(Iterations on MultiSeed with three Seeds)***\n1. We create *Initializator* hash by hashing a constant-*Basis* with `ImageSize` (*Width*/*Height*);\n2. We hash *Initializator* with **Seed**`(1)` \u2014 that is our ***first*** **prngS**`(1)` (PRNG Seed);\n3. We hash **prngS**`(1)` with **Seed**`(2)`, \u2014 that is our ***second*** PRNG Seed;\n4. We hash **prngS**`(2)` with **Seed**`(3)`, \u2014 that is our ***third*** PRNG Seed.\n\nIt means that **Seeds are dependent on each other** and *Initializator* **depends on the Image Width & Height**, thus, each unique-sized Image will utilize **different Seed values for PRNG**. This will add some protection against the brute-force or seed re-usage.\n\n###### This can be disabled with `use_raw_seed=True` on `LSB_MWS`, though *not* recommended.\n\n## Installation\n\n**You can install SteganoN with PIP**\n```bash\npip install steganon\n```\n**Or you can install it after `git clone`**\n```bash\npython -m venv steganon-env\ncd steganon-env && . bin/activate\n\ngit clone https://github.com/NonProjects/steganon\npip install ./steganon\n```\n**SteganoN has basic CLI implementation**\n```bash\n# Install SteganoN with CLI\npip install steganon[cli]\n```\n\n\n## Example\n\n### (One Seed) Hiding Secret Data\n\n```python3\nfrom steganon import Image, LSB_MWS\n\nimage = Image.open('example.png') # Open targeted Image\nlsb_mws = LSB_MWS(image, b'seed_0') # Init with seed=b'seed_0'\n\nlsb_mws.hide(b'Secret!!!') # Write secret message to image pixels\nimage.save('example.png') # Save altered image with secret data\n```\n\n### (One Seed) Extracting Secret Data\n```python\nfrom steganon import Image, LSB_MWS\n\nimage = Image.open('example.png') # Open Image with hidden data\nlsb_mws = LSB_MWS(image, b'seed_0') # Init with seed=b'seed_0'\n\nprint(lsb_mws.extract()) # b'Secret!!!'\n```\n\n### (MultiSeed) Hiding Secret Data\n\n```python3\nfrom steganon import Image, LSB_MWS\n\nimage = Image.open('example.png') # Open targeted Image\nseeds = (b'seed_0', b'seed_1', b'seed_2') # You can use as many as you want\nlsb_mws = LSB_MWS(image, seeds) # Init LSB_MWS with multiple seeds\n\nlsb_mws.hide(b'Secret data on Seed(0)!!!') # Write secret message to image pixels\nlsb_mws.next() # Switch to the next Seed (b'seed_1')\n\nlsb_mws.hide(b'Secret data on Seed(1)!!!') # Write secret message to image pixels\nlsb_mws.next() # Switch to the next Seed (b'seed_2')\n\nlsb_mws.hide(b'Secret data on Seed(2)!!!') # Write secret message to image pixels\nimage.save('example.png') # Save altered image with secret data\n```\n\n### (MultiSeed) Extracting Secret Data\n```python\nfrom steganon import Image, LSB_MWS\n\nimage = Image.open('example.png') # Open targeted Image\nseeds = (b'seed_0', b'seed_1', b'seed_2') # You can use as many as you want\nlsb_mws = LSB_MWS(image, seeds) # Init LSB_MWS with multiple seeds\n\nprint(lsb_mws.extract()) # b'Secret data on Seed(0)!!!'\nlsb_mws.next() # Switch to the next Seed (b'seed_1')\nprint(lsb_mws.extract()) # b'Secret data on Seed(1)!!!'\nlsb_mws.next() # Switch to the next Seed (b'seed_2')\nprint(lsb_mws.extract()) # b'Secret data on Seed(2)!!!'\n```\n\n## Image Comparison\n\nHere is comparison between [**Original image (1)**](https://github.com/user-attachments/assets/2d639687-718e-4868-afbb-fc431b35e747) and [**Image with written on pixels data (2)**](https://github.com/user-attachments/assets/4b3de992-2fcb-4c54-84e2-94546cd0c168).\n\n<img src=\"https://github.com/user-attachments/assets/b218038d-94c6-41bc-b471-9567112e6c6e\" width=\"777\" height=\"-1\"></img>\n\n[**Modified Image (2)**](https://github.com/user-attachments/assets/4b3de992-2fcb-4c54-84e2-94546cd0c168) has whole [**Zen of Python**](https://peps.python.org/pep-0020/#the-zen-of-python) written on it.\\\nYou can extract Zen from **(2)** by using Seed `b'spam_eggs'`\n\n## TestMode on LSB\\_MWS\n\n`steganon.LSB_MWS` class has a `testmode` key. We can use it to check affected pixels under different seeds\n\n<img src=\"https://github.com/user-attachments/assets/a2e1beda-f205-432e-8c2e-56dfb0d8ead7\" width=\"777\" height=\"-1\"></img>\n\n## Additional Information\n\n**Tested formats:**\n\n* \u2705 **PNG**\n* \u2705 **BMP**\n* \u2705 **TIFF**\n* \u2705 **WEBP**\n* \u2705 **JPEG2000**\n* \u274c **JPG**\n* \u274c **HEIF**\n\n0. This library & implementation **wasn't** verified by steganography experts. **Use with caution!**\n1. **Always use a different seed!** Pixel positions `*`may be the same on different Images and Data!\n2. This library **will not** work with JPEG due to its lossy design. **Use lossless formats** (e.g PNG, WEBP, etc);\n3. Best `**`template to hide data is a **compressed JPEG turned to PNG**. Library has `tools.pngify`, use it;\n4. The **bigger your Image**, the **bigger amount of Data you can hide** (though the less data\u2014the `***`better).\n\n`* ` If Image Width/Height and Seed are the same. **Always use a unique Seed**\\\n`** ` Your cover Image **should** have a decent amount of \"Noise\" / Compression\\\n`***` Quite obviously, **the lower coverage**, the **less chance for analyze tools**\n\nContact me on **thenonproton@pm.me** (or just [**open Issue**](https://github.com/NonProjects/steganon/issues)) if you have any feedback on this library.\n\n## All Aboard!\n\nTry to download [**this example image**](https://github.com/user-attachments/assets/cd1e1785-fead-4a80-83c7-f3400334c756) and extract secret information from it with seed `b'OZZY'`\\\nSave data to the file with `.ogg` extension and play it with your favourite media player.\n\n###### *Flying high again!*\n",
"bugtrack_url": null,
"license": "LGPL-2.1",
"summary": "LSB Matching Steganography with PRNG & Deniable Hide",
"version": "1.1",
"project_urls": {
"Homepage": "https://github.com/NonProjects/steganon"
},
"split_keywords": [
"steganography",
" lsb",
" cryptography"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "e0f567ceb8c814297a6172d25a9de1eac05804c80d1727b6d24db635b51df47a",
"md5": "852df1571a80b72b0b1c9288c9279e33",
"sha256": "3375267f1b4f4a4e138ab0d18fb3e3b8fe2f8540ce0d249d0d363027634fcccc"
},
"downloads": -1,
"filename": "steganon-1.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "852df1571a80b72b0b1c9288c9279e33",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": null,
"size": 30611,
"upload_time": "2025-10-10T23:01:16",
"upload_time_iso_8601": "2025-10-10T23:01:16.785433Z",
"url": "https://files.pythonhosted.org/packages/e0/f5/67ceb8c814297a6172d25a9de1eac05804c80d1727b6d24db635b51df47a/steganon-1.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "bbcedb7e48dfbc953f98f0c0033ef18ce2ffc85f3e18cc4f5e872f2b001c2f1c",
"md5": "9db8944b56e5ff0cb0b9da4ed4ccf2fe",
"sha256": "bfad2af175e75d3457f70eea2da524e4c972a4649bf63552b8a8fd35ce8dbf2a"
},
"downloads": -1,
"filename": "steganon-1.1.tar.gz",
"has_sig": false,
"md5_digest": "9db8944b56e5ff0cb0b9da4ed4ccf2fe",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 31897,
"upload_time": "2025-10-10T23:01:18",
"upload_time_iso_8601": "2025-10-10T23:01:18.538635Z",
"url": "https://files.pythonhosted.org/packages/bb/ce/db7e48dfbc953f98f0c0033ef18ce2ffc85f3e18cc4f5e872f2b001c2f1c/steganon-1.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-10-10 23:01:18",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "NonProjects",
"github_project": "steganon",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "steganon"
}