Stegano is a Go library that enables the embedding and extraction of data within images using steganographic techniques. It supports any image compatible with Go's image.Image
type, includes ZSTD compression for optimizing data storage, and also offers encryption for securely hiding information.
- Features
- What is Steganography?
- Use Cases
- Installation
- Usage
- Working with Images
- Advanced Options
- Notes
- Benchmarks
- Future Improvements
- Multi-Image Support: Works with any image type compatible with Go's
image.Image
. - Data Compression: Supports ZSTD compression to minimize the size of embedded data.
- Capacity Calculation: Automatically calculates the maximum capacity of an image for data embedding.
- Variable Depth Encoding: Allows you to embed data up to a specified bit depth.
- Concurrency: Supports concurrent processing for improved speed (higher memory usage).
- Custom Bit Depth Embedding: Lets you specify the bit depth used for data embedding (e.g., LSB, MSB).
- Encryption: Enables secure encryption of data before embedding into the image.
- Efficient PNG Encoding: Saves the image in PNG format.
Steganography is the practice of concealing data within other, seemingly innocent data in such a way that it remains undetectable to the casual observer. Unlike encryption, which focuses on making data unreadable, steganography hides it entirely, making the data appear normal and undisturbed.
The most common use case for steganography is hiding text or binary data within an image, audio file, or video. This library enables you to easily embed and extract such hidden information from images.
To integrate Stegano into your Go project, simply run:
go get github.com/scott-mescudi/stegano@latest
import (
"github.com/scott-mescudi/stegano"
)
The following are high-level functions for embedding and extracting data easily:
func main() {
err := stegano.EmbedFile("cover.png", "data.txt", stegano.DefaultOutputFile, "password123", stegano.LSB)
if err != nil {
log.Fatalln("Error:", err)
}
}
func main() {
err := stegano.ExtractFile(stegano.DefaultOutputFile, "password123", stegano.LSB)
if err != nil {
log.Fatalln("Error:", err)
}
}
For more control over the process, refer to the examples below:
You can embed a message into an image using the EmbedHandler
class.
func main() {
coverFile, err := stegano.Decodeimage("coverimage.png")
if err != nil {
log.Fatalln(err)
}
embedder := stegano.NewEmbedHandler()
// Encode and save the message in the cover image.
err = embedder.EncodeAndSave(coverFile, []byte("Hello, World!"), stegano.MinBitDepth, stegano.DefaultpngOutputFile, true)
if err != nil {
log.Fatalln(err)
}
}
Extract a hidden message from an image using the ExtractHandler
class.
func main() {
coverFile, err := stegano.Decodeimage("embeddedimage.png")
if err != nil {
log.Fatalln(err)
}
extractor := stegano.NewExtractHandler()
// Decode the message from the image.
data, err := extractor.Decode(coverFile, stegano.MinBitDepth, true)
if err != nil {
log.Fatalln(err)
}
// Print the extracted message.
fmt.Println(string(data))
}
Embed data into an image without using any compression.
func main() {
coverFile, err := stegano.Decodeimage("coverimage.png")
if err != nil {
log.Fatalln(err)
}
embedder := stegano.NewEmbedHandler()
// Embed the message into the image without compression.
embeddedImage, err := embedder.EmbedDataIntoImage(coverFile, []byte("Hello, World!"), stegano.MinBitDepth)
if err != nil {
log.Fatalln(err)
}
err = stegano.SaveImage(stegano.DefaultpngOutputFile, embeddedImage)
if err != nil {
log.Fatalln(err)
}
}
Extract data from an image where no compression was used.
func main() {
coverFile, err := stegano.Decodeimage("embeddedimage.png")
if err != nil {
log.Fatalln(err)
}
extractor := stegano.NewExtractHandler()
// Extract uncompressed data from the image.
data, err := extractor.ExtractDataFromImage(coverFile, stegano.MinBitDepth)
if err != nil {
log.Fatalln(err)
}
// Print the extracted message.
fmt.Println(string(data))
}
Embed data at a specific bit depth for better control over the hiding technique.
func main() {
coverFile, err := stegano.Decodeimage("coverimage.png")
if err != nil {
log.Fatalln(err)
}
embedder := stegano.NewEmbedHandler()
// Embed the message at a specific bit depth (e.g., 3).
embeddedImage, err := embedder.EmbedAtDepth(coverFile, []byte("Hello, World!"), 3)
if err != nil {
log.Fatalln(err)
}
err = stegano.SaveImage(stegano.DefaultpngOutputFile, embeddedImage)
if err != nil {
log.Fatalln(err)
}
}
Extract data from an image using the same bit depth used for embedding.
func main() {
coverFile, err := stegano.Decodeimage("embeddedimage.png")
if err != nil {
log.Fatalln(err)
}
extractor := stegano.NewExtractHandler()
// Extract data from the image at a specific bit depth (e.g., 3).
data, err := extractor.ExtractAtDepth(coverFile, 3)
if err != nil {
log.Fatalln(err)
}
// Print the extracted message.
fmt.Println(string(data))
}
Check how much data an image can hold based on the bit depth.
func main() {
coverFile, err := stegano.Decodeimage("embeddedimage.png")
if err != nil {
log.Fatalln(err)
}
// Calculate and print the data capacity of the image.
capacity := stegano.GetImageCapacity(coverFile, stegano.MinBitDepth)
fmt.Printf("Image capacity at bit depth %d: %d bytes\n", stegano.MinBitDepth, capacity)
}
Encrypt the data before embedding it into the image for added security.
func main() {
coverFile, err := stegano.Decodeimage("coverimage.png")
if err != nil {
log.Fatalln(err)
}
// Encrypt the data before embedding.
encryptedData, err := stegano.EncryptData([]byte("Hello, World!"), "your-encryption-key")
if err != nil {
log.Fatalln(err)
}
embedder := stegano.NewEmbedHandler()
// Embed the encrypted data into the image.
err = embedder.EncodeAndSave(coverFile, encryptedData, stegano.MinBitDepth, stegano.DefaultpngOutputFile, true)
if err != nil {
log.Fatalln(err)
}
}
Extract encrypted data from the image and decrypt it.
func main() {
coverFile, err := stegano.Decodeimage("embeddedimage.png")
if err != nil {
log.Fatalln(err)
}
extractor := stegano.NewExtractHandler()
// Extract the encrypted data.
encryptedData, err := extractor.Decode(coverFile, stegano.MinBitDepth, true)
if err != nil {
log.Fatalln(err)
}
// Decrypt the data.
decryptedData, err := stegano.DecryptData(encryptedData, "your-encryption-key")
if err != nil {
log.Fatalln(err)
}
// Print the decrypted message.
fmt.Println(string(decryptedData))
}
You can use concurrency to speed up the embedding and extraction process. Use the
NewEmbedHandlerWithConcurrency
orNewExtractHandlerWithConcurrency
functions to specify the number of goroutines to be used.
embedder, err := stegano.NewEmbedHandlerWithConcurrency(12)
if err != nil {
log.Fatalln(err)
}
extractor, err := stegano.NewExtractHandlerWithConcurrency(12)
if err != nil {
log.Fatalln(err)
}
- This library CAN be used with any image type but works best with PNG images.
- Bit Depth and Compression: Ensure that the same bit depth and compression settings are used during both embedding and extraction.
- Default Output Format: By default, images NEED to be saved in PNG format to avoid any data loss.
Library Name | Test 1 (ns/op) | Test 2 (ns/op) | Test 3 (ns/op) | Avg Time (ns/op) | Avg Time (ms/op) |
---|---|---|---|---|---|
Stegano | 352,531,567 | 349,444,200 | 348,196,967 | 350,390,578 | 350.39 ms |
Stegano with Concurrency | 286,168,125 | 293,260,925 | 284,079,175 | 287,169,408 | 287.17 ms |
auyer/steganography | 1,405,256,700 | 1,424,957,200 | 1,401,682,600 | 1,410,965,500 | 1,410.97 ms |
Image size: 10,473,459 bytes
Text size: 641,788 bytes
Benchmark code can be found here
- Huffman Encoding: Add support for more efficient compression techniques like Huffman coding.
- Audio Support: Extend support to audio file formats.
- Multi-Carrier Support: Enable splitting data across multiple images or files for larger data embedding.