29 KiB
Stegasoo REST API Documentation (v3.2.0)
Complete REST API reference for Stegasoo steganography operations.
Table of Contents
- Overview
- What's New in v3.2.0
- Installation
- Authentication
- Base URL
- Endpoints
- Embedding Modes
- Data Models
- Error Handling
- Code Examples
- Rate Limiting
- Security Considerations
Overview
The Stegasoo REST API provides programmatic access to all steganography operations:
- Generate credentials (passphrase, PINs, RSA keys)
- Encode messages or files into images (LSB or DCT mode)
- Decode messages or files from images (auto-detects mode)
- Analyze image capacity and compare modes
The API supports both JSON (base64-encoded images) and multipart form data (direct file uploads).
What's New in v3.2.0
Version 3.2.0 introduces breaking changes to simplify the API:
| Change | Before (v3.1) | After (v3.2.0) |
|---|---|---|
| Passphrase | Daily rotation (phrases dict) |
Single passphrase string |
| Date parameter | date_str required/optional |
Removed entirely |
| Field name | day_phrase |
passphrase |
| Default words | 3 words | 4 words |
Key benefits:
- ✅ No need to track encoding dates
- ✅ Simpler request/response models
- ✅ True asynchronous communications
- ✅ Stronger default security (4 words = ~44 bits)
Breaking Change: v3.2.0 cannot decode images created with v3.1.x due to different key derivation.
Installation
From PyPI
pip install stegasoo[api]
This automatically installs DCT dependencies (scipy) for full functionality.
From Source
git clone https://github.com/example/stegasoo.git
cd stegasoo
pip install -e ".[api]"
Running the Server
Development:
cd frontends/api
python main.py
Production:
cd frontends/api
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4
Docker:
docker-compose up api
Authentication
The API currently operates without authentication. For production deployments, implement authentication at the reverse proxy level (nginx, Caddy) or add API key middleware.
Base URL
| Environment | URL |
|---|---|
| Local Development | http://localhost:8000 |
| Docker | http://localhost:8000 |
| Production | Configure as needed |
Endpoints
GET / (Status)
Check API status and configuration.
Request
GET / HTTP/1.1
Host: localhost:8000
Response
{
"version": "3.2.0",
"has_argon2": true,
"has_qrcode_read": true,
"has_dct": true,
"max_payload_kb": 500,
"available_modes": ["lsb", "dct"],
"dct_features": {
"output_formats": ["png", "jpeg"],
"color_modes": ["grayscale", "color"],
"default_output_format": "png",
"default_color_mode": "grayscale"
},
"breaking_changes": {
"date_removed": "No date_str parameter needed - encode/decode anytime",
"passphrase_renamed": "day_phrase → passphrase (single passphrase, no daily rotation)",
"format_version": 4,
"backward_compatible": false
}
}
Response Fields
| Field | Type | Description |
|---|---|---|
version |
string | Stegasoo library version |
has_argon2 |
boolean | Whether Argon2id is available |
has_qrcode_read |
boolean | Whether QR code reading is available |
has_dct |
boolean | Whether DCT mode is available (scipy) |
max_payload_kb |
integer | Maximum payload size in KB |
available_modes |
array | Available embedding modes |
dct_features |
object | DCT mode options (if available) |
breaking_changes |
object | v3.2.0 breaking changes info |
cURL Example
curl http://localhost:8000/
GET /modes
Get available embedding modes and their status.
Request
GET /modes HTTP/1.1
Host: localhost:8000
Response
{
"lsb": {
"available": true,
"name": "Spatial LSB",
"description": "Embed in pixel LSBs, outputs PNG/BMP",
"output_format": "PNG (color)",
"capacity_ratio": "100%"
},
"dct": {
"available": true,
"name": "DCT Domain",
"description": "Embed in DCT coefficients, frequency domain steganography",
"output_formats": ["png", "jpeg"],
"color_modes": ["grayscale", "color"],
"capacity_ratio": "~20% of LSB",
"requires": "scipy"
}
}
POST /generate
Generate credentials for encoding/decoding.
Request
POST /generate HTTP/1.1
Host: localhost:8000
Content-Type: application/json
{
"use_pin": true,
"use_rsa": false,
"pin_length": 6,
"rsa_bits": 2048,
"words_per_passphrase": 4
}
Request Body
| Field | Type | Default | Description |
|---|---|---|---|
use_pin |
boolean | true |
Generate a PIN |
use_rsa |
boolean | false |
Generate an RSA key |
pin_length |
integer | 6 |
PIN length (6-9) |
rsa_bits |
integer | 2048 |
RSA key size (2048, 3072, 4096) |
words_per_passphrase |
integer | 4 |
Words per passphrase (3-12) |
Response
{
"passphrase": "abandon ability able about",
"pin": "847293",
"rsa_key_pem": null,
"entropy": {
"passphrase": 44,
"pin": 19,
"rsa": 0,
"total": 63
},
"phrases": null
}
Response Fields
| Field | Type | Description |
|---|---|---|
passphrase |
string | Single passphrase (v3.2.0) |
pin |
string|null | Generated PIN (if requested) |
rsa_key_pem |
string|null | PEM-encoded RSA key (if requested) |
entropy.passphrase |
integer | Entropy from passphrase (bits) |
entropy.pin |
integer | Entropy from PIN (bits) |
entropy.rsa |
integer | Entropy from RSA key (bits) |
entropy.total |
integer | Combined entropy (bits) |
phrases |
null | Deprecated field (always null in v3.2.0) |
cURL Examples
Default (PIN with 4-word passphrase):
curl -X POST http://localhost:8000/generate \
-H "Content-Type: application/json" \
-d '{"use_pin": true}'
RSA only with 6-word passphrase:
curl -X POST http://localhost:8000/generate \
-H "Content-Type: application/json" \
-d '{"use_pin": false, "use_rsa": true, "rsa_bits": 4096, "words_per_passphrase": 6}'
Both PIN and RSA:
curl -X POST http://localhost:8000/generate \
-H "Content-Type: application/json" \
-d '{
"use_pin": true,
"use_rsa": true,
"pin_length": 9,
"rsa_bits": 4096,
"words_per_passphrase": 6
}'
POST /encode (JSON)
Encode a text message using base64-encoded images.
Request
POST /encode HTTP/1.1
Host: localhost:8000
Content-Type: application/json
{
"message": "Secret message here",
"reference_photo_base64": "iVBORw0KGgo...",
"carrier_image_base64": "iVBORw0KGgo...",
"passphrase": "apple forest thunder mountain",
"pin": "123456",
"rsa_key_base64": null,
"rsa_password": null,
"embed_mode": "lsb",
"dct_output_format": "png",
"dct_color_mode": "grayscale"
}
Request Body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
message |
string | ✓ | Message to encode | |
reference_photo_base64 |
string | ✓ | Base64-encoded reference photo | |
carrier_image_base64 |
string | ✓ | Base64-encoded carrier image | |
passphrase |
string | ✓ | Passphrase (v3.2.0) | |
pin |
string | * | Static PIN (6-9 digits) | |
rsa_key_base64 |
string | * | Base64-encoded RSA key PEM | |
rsa_password |
string | Password for RSA key | ||
embed_mode |
string | "lsb" |
"lsb" or "dct" |
|
dct_output_format |
string | "png" |
"png" or "jpeg" (DCT only) |
|
dct_color_mode |
string | "grayscale" |
"grayscale" or "color" (DCT only) |
* At least one of pin or rsa_key_base64 required.
Response
{
"stego_image_base64": "iVBORw0KGgo...",
"filename": "a1b2c3d4.png",
"capacity_used_percent": 12.4,
"embed_mode": "lsb",
"output_format": "png",
"color_mode": "color",
"date_used": null,
"day_of_week": null
}
Response Fields
| Field | Type | Description |
|---|---|---|
stego_image_base64 |
string | Base64-encoded stego image |
filename |
string | Suggested filename |
capacity_used_percent |
float | Percentage of capacity used |
embed_mode |
string | Mode used: "lsb" or "dct" |
output_format |
string | Output format: "png" or "jpeg" |
color_mode |
string | Color mode: "color" or "grayscale" |
date_used |
null | Deprecated (always null in v3.2.0) |
day_of_week |
null | Deprecated (always null in v3.2.0) |
cURL Example (LSB Mode)
# Prepare base64-encoded images
REF_B64=$(base64 -w0 reference.jpg)
CARRIER_B64=$(base64 -w0 carrier.png)
curl -X POST http://localhost:8000/encode \
-H "Content-Type: application/json" \
-d "{
\"message\": \"Secret message\",
\"reference_photo_base64\": \"$REF_B64\",
\"carrier_image_base64\": \"$CARRIER_B64\",
\"passphrase\": \"apple forest thunder mountain\",
\"pin\": \"123456\"
}" | jq -r '.stego_image_base64' | base64 -d > stego.png
cURL Example (DCT Mode with JPEG)
curl -X POST http://localhost:8000/encode \
-H "Content-Type: application/json" \
-d "{
\"message\": \"Secret message\",
\"reference_photo_base64\": \"$REF_B64\",
\"carrier_image_base64\": \"$CARRIER_B64\",
\"passphrase\": \"apple forest thunder mountain\",
\"pin\": \"123456\",
\"embed_mode\": \"dct\",
\"dct_output_format\": \"jpeg\",
\"dct_color_mode\": \"color\"
}" | jq -r '.stego_image_base64' | base64 -d > stego.jpg
POST /encode/file
Encode a binary file using base64-encoded data.
Request
POST /encode/file HTTP/1.1
Host: localhost:8000
Content-Type: application/json
{
"file_data_base64": "JVBERi0xLjQK...",
"filename": "secret.pdf",
"mime_type": "application/pdf",
"reference_photo_base64": "iVBORw0KGgo...",
"carrier_image_base64": "iVBORw0KGgo...",
"passphrase": "apple forest thunder mountain",
"pin": "123456",
"embed_mode": "lsb"
}
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
file_data_base64 |
string | ✓ | Base64-encoded file data |
filename |
string | ✓ | Original filename |
mime_type |
string | MIME type of file | |
reference_photo_base64 |
string | ✓ | Base64-encoded reference photo |
carrier_image_base64 |
string | ✓ | Base64-encoded carrier image |
passphrase |
string | ✓ | Passphrase |
pin |
string | * | Static PIN |
rsa_key_base64 |
string | * | Base64-encoded RSA key |
embed_mode |
string | "lsb" or "dct" |
|
dct_output_format |
string | "png" or "jpeg" |
|
dct_color_mode |
string | "grayscale" or "color" |
Response
Same as /encode endpoint.
POST /encode/multipart
Encode using direct file uploads. Returns the stego image directly.
Form Fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
passphrase |
string | ✓ | Passphrase (v3.2.0) | |
reference_photo |
file | ✓ | Reference photo file | |
carrier |
file | ✓ | Carrier image file | |
message |
string | * | Text message to encode | |
payload_file |
file | * | Binary file to embed | |
pin |
string | ** | Static PIN | |
rsa_key |
file | ** | RSA key file (.pem) | |
rsa_key_qr |
file | ** | RSA key from QR code image | |
rsa_password |
string | Password for RSA key | ||
embed_mode |
string | "lsb" |
"lsb" or "dct" |
|
dct_output_format |
string | "png" |
"png" or "jpeg" |
|
dct_color_mode |
string | "grayscale" |
"grayscale" or "color" |
* At least one of message or payload_file required.
** At least one of pin, rsa_key, or rsa_key_qr required.
Response
Returns the image directly with headers:
HTTP/1.1 200 OK
Content-Type: image/png
Content-Disposition: attachment; filename="a1b2c3d4.png"
X-Stegasoo-Capacity-Percent: 12.4
X-Stegasoo-Embed-Mode: lsb
X-Stegasoo-Output-Format: png
X-Stegasoo-Color-Mode: color
X-Stegasoo-Version: 3.2.0
<binary image data>
Response Headers
| Header | Description |
|---|---|
Content-Type |
image/png or image/jpeg |
Content-Disposition |
Suggested filename |
X-Stegasoo-Capacity-Percent |
Capacity percentage used |
X-Stegasoo-Embed-Mode |
lsb or dct |
X-Stegasoo-Output-Format |
png or jpeg |
X-Stegasoo-Color-Mode |
color or grayscale |
X-Stegasoo-Version |
API version |
cURL Example (LSB)
curl -X POST http://localhost:8000/encode/multipart \
-F "passphrase=apple forest thunder mountain" \
-F "pin=123456" \
-F "message=Secret message" \
-F "reference_photo=@reference.jpg" \
-F "carrier=@carrier.png" \
--output stego.png
cURL Example (DCT + JPEG)
curl -X POST http://localhost:8000/encode/multipart \
-F "passphrase=apple forest thunder mountain" \
-F "pin=123456" \
-F "message=Secret message for social media" \
-F "embed_mode=dct" \
-F "dct_output_format=jpeg" \
-F "dct_color_mode=color" \
-F "reference_photo=@reference.jpg" \
-F "carrier=@carrier.png" \
--output stego.jpg
cURL Example (Embed File)
curl -X POST http://localhost:8000/encode/multipart \
-F "passphrase=apple forest thunder mountain" \
-F "pin=123456" \
-F "payload_file=@secret.pdf" \
-F "reference_photo=@reference.jpg" \
-F "carrier=@carrier.png" \
--output stego.png
POST /decode (JSON)
Decode a message or file using base64-encoded images. Auto-detects embedding mode.
Request
POST /decode HTTP/1.1
Host: localhost:8000
Content-Type: application/json
{
"stego_image_base64": "iVBORw0KGgo...",
"reference_photo_base64": "iVBORw0KGgo...",
"passphrase": "apple forest thunder mountain",
"pin": "123456",
"rsa_key_base64": null,
"rsa_password": null,
"embed_mode": "auto"
}
Request Body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
stego_image_base64 |
string | ✓ | Base64-encoded stego image | |
reference_photo_base64 |
string | ✓ | Base64-encoded reference photo | |
passphrase |
string | ✓ | Passphrase | |
pin |
string | * | Static PIN | |
rsa_key_base64 |
string | * | Base64-encoded RSA key | |
rsa_password |
string | Password for RSA key | ||
embed_mode |
string | "auto" |
"auto", "lsb", or "dct" |
* Must match security factors used during encoding.
Response (Text)
{
"payload_type": "text",
"message": "Secret message here",
"file_data_base64": null,
"filename": null,
"mime_type": null
}
Response (File)
{
"payload_type": "file",
"message": null,
"file_data_base64": "JVBERi0xLjQK...",
"filename": "secret.pdf",
"mime_type": "application/pdf"
}
Response Fields
| Field | Type | Description |
|---|---|---|
payload_type |
string | "text" or "file" |
message |
string|null | Decoded message (if text) |
file_data_base64 |
string|null | Base64-encoded file (if file) |
filename |
string|null | Original filename (if file) |
mime_type |
string|null | MIME type (if file) |
cURL Example
STEGO_B64=$(base64 -w0 stego.png)
REF_B64=$(base64 -w0 reference.jpg)
curl -X POST http://localhost:8000/decode \
-H "Content-Type: application/json" \
-d "{
\"stego_image_base64\": \"$STEGO_B64\",
\"reference_photo_base64\": \"$REF_B64\",
\"passphrase\": \"apple forest thunder mountain\",
\"pin\": \"123456\"
}"
POST /decode/multipart
Decode using direct file uploads. Auto-detects embedding mode.
Form Fields
| Field | Type | Required | Description |
|---|---|---|---|
passphrase |
string | ✓ | Passphrase |
reference_photo |
file | ✓ | Reference photo file |
stego_image |
file | ✓ | Stego image file |
pin |
string | * | Static PIN |
rsa_key |
file | * | RSA key file (.pem) |
rsa_key_qr |
file | * | RSA key from QR code image |
rsa_password |
string | Password for RSA key | |
embed_mode |
string | "auto", "lsb", or "dct" |
Response
Same JSON format as /decode endpoint.
cURL Example
curl -X POST http://localhost:8000/decode/multipart \
-F "passphrase=apple forest thunder mountain" \
-F "pin=123456" \
-F "reference_photo=@reference.jpg" \
-F "stego_image=@stego.png"
POST /compare
Compare LSB and DCT embedding modes for a carrier image.
Request
POST /compare HTTP/1.1
Host: localhost:8000
Content-Type: application/json
{
"carrier_image_base64": "iVBORw0KGgo...",
"payload_size": 50000
}
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
carrier_image_base64 |
string | ✓ | Base64-encoded carrier image |
payload_size |
integer | Optional payload size to check |
Response
{
"width": 1920,
"height": 1080,
"lsb": {
"capacity_bytes": 776970,
"capacity_kb": 758.8,
"available": true,
"output_format": "PNG"
},
"dct": {
"capacity_bytes": 64800,
"capacity_kb": 63.3,
"available": true,
"output_formats": ["png", "jpeg"],
"color_modes": ["grayscale", "color"],
"ratio_vs_lsb_percent": 8.3
},
"payload_check": {
"size_bytes": 50000,
"fits_lsb": true,
"fits_dct": true
},
"recommendation": "dct (payload fits, better stealth)"
}
POST /will-fit
Check if a payload of given size will fit in a carrier image.
Request
POST /will-fit HTTP/1.1
Host: localhost:8000
Content-Type: application/json
{
"carrier_image_base64": "iVBORw0KGgo...",
"payload_size": 50000,
"embed_mode": "lsb"
}
Response
{
"fits": true,
"payload_size": 50000,
"capacity": 776970,
"usage_percent": 6.4,
"headroom": 726970,
"mode": "lsb"
}
POST /image/info
Get image information and capacity.
Request (Multipart)
curl -X POST http://localhost:8000/image/info \
-F "image=@carrier.png"
Response
{
"width": 1920,
"height": 1080,
"pixels": 2073600,
"capacity_bytes": 776970,
"capacity_kb": 758,
"modes": {
"lsb": {
"capacity_bytes": 776970,
"capacity_kb": 758.8,
"available": true,
"output_format": "PNG"
},
"dct": {
"capacity_bytes": 64800,
"capacity_kb": 63.3,
"available": true,
"output_format": "PNG/JPEG (grayscale or color)"
}
}
}
POST /extract-key-from-qr
Extract RSA key from a QR code image.
Request (Multipart)
curl -X POST http://localhost:8000/extract-key-from-qr \
-F "qr_image=@keyqr.png"
Response
{
"success": true,
"key_pem": "-----BEGIN PRIVATE KEY-----\n...",
"error": null
}
Embedding Modes
LSB Mode (Default)
Least Significant Bit embedding modifies pixel values directly.
| Aspect | Details |
|---|---|
| Parameter | "embed_mode": "lsb" |
| Capacity | ~3 bits/pixel (~375 KB for 1920×1080) |
| Output | PNG only (lossless required) |
| Resilience | ❌ Destroyed by JPEG compression |
| Best For | Maximum capacity, controlled channels |
DCT Mode
Discrete Cosine Transform embedding hides data in frequency coefficients.
| Aspect | Details |
|---|---|
| Parameter | "embed_mode": "dct" |
| Capacity | ~0.25 bits/pixel (~65 KB for 1920×1080) |
| Output | PNG or JPEG |
| Resilience | ✅ Better resistance to analysis |
| Best For | Stealth requirements |
DCT Options
| Option | Values | Default | Description |
|---|---|---|---|
dct_output_format |
"png", "jpeg" |
"png" |
Output image format |
dct_color_mode |
"grayscale", "color" |
"grayscale" |
Color processing mode |
Capacity Comparison
| Mode | 1920×1080 Capacity |
|---|---|
| LSB (PNG) | ~375 KB |
| DCT (PNG) | ~65 KB |
| DCT (JPEG) | ~50 KB |
Data Models
GenerateRequest
{
"use_pin": true,
"use_rsa": false,
"pin_length": 6,
"rsa_bits": 2048,
"words_per_passphrase": 4
}
GenerateResponse
{
"passphrase": "word1 word2 word3 word4",
"pin": "123456",
"rsa_key_pem": null,
"entropy": {"passphrase": 44, "pin": 19, "rsa": 0, "total": 63},
"phrases": null
}
EncodeRequest
{
"message": "string",
"reference_photo_base64": "string",
"carrier_image_base64": "string",
"passphrase": "string",
"pin": "string",
"rsa_key_base64": "string",
"rsa_password": "string",
"embed_mode": "lsb",
"dct_output_format": "png",
"dct_color_mode": "grayscale"
}
EncodeResponse
{
"stego_image_base64": "string",
"filename": "string",
"capacity_used_percent": 12.4,
"embed_mode": "lsb",
"output_format": "png",
"color_mode": "color",
"date_used": null,
"day_of_week": null
}
DecodeRequest
{
"stego_image_base64": "string",
"reference_photo_base64": "string",
"passphrase": "string",
"pin": "string",
"rsa_key_base64": "string",
"rsa_password": "string",
"embed_mode": "auto"
}
DecodeResponse
{
"payload_type": "text",
"message": "string",
"file_data_base64": null,
"filename": null,
"mime_type": null
}
ErrorResponse
{
"detail": "Error message describing the problem"
}
Error Handling
HTTP Status Codes
| Code | Meaning | Use Case |
|---|---|---|
| 200 | OK | Successful operation |
| 400 | Bad Request | Invalid input, capacity error |
| 401 | Unauthorized | Decryption failed (wrong credentials) |
| 500 | Internal Error | Unexpected server error |
| 501 | Not Implemented | Feature unavailable (e.g., QR without pyzbar) |
Common Errors
| Status | Error | Solution |
|---|---|---|
| 400 | "Must enable at least one of use_pin or use_rsa" | Set use_pin or use_rsa to true |
| 400 | "Carrier image too small" | Use larger carrier image |
| 400 | "DCT mode requires scipy" | Install scipy |
| 400 | "embed_mode must be 'lsb' or 'dct'" | Fix embed_mode value |
| 401 | "Decryption failed. Check credentials." | Verify passphrase, PIN, ref photo |
| 501 | "QR code reading not available" | Install pyzbar and libzbar |
Code Examples
Python with requests
import base64
import requests
BASE_URL = "http://localhost:8000"
# Generate credentials
response = requests.post(f"{BASE_URL}/generate", json={
"use_pin": True,
"words_per_passphrase": 4
})
creds = response.json()
print(f"Passphrase: {creds['passphrase']}")
print(f"PIN: {creds['pin']}")
# Encode using multipart (LSB mode)
with open("reference.jpg", "rb") as ref, open("carrier.png", "rb") as carrier:
response = requests.post(f"{BASE_URL}/encode/multipart", files={
"reference_photo": ref,
"carrier": carrier,
}, data={
"message": "Secret message",
"passphrase": "apple forest thunder mountain",
"pin": "123456"
})
with open("stego.png", "wb") as f:
f.write(response.content)
# Encode with DCT for social media
with open("reference.jpg", "rb") as ref, open("carrier.png", "rb") as carrier:
response = requests.post(f"{BASE_URL}/encode/multipart", files={
"reference_photo": ref,
"carrier": carrier,
}, data={
"message": "Secret message for Instagram",
"passphrase": "apple forest thunder mountain",
"pin": "123456",
"embed_mode": "dct",
"dct_output_format": "jpeg",
"dct_color_mode": "color"
})
with open("stego_social.jpg", "wb") as f:
f.write(response.content)
# Decode (auto-detects mode)
with open("reference.jpg", "rb") as ref, open("stego.png", "rb") as stego:
response = requests.post(f"{BASE_URL}/decode/multipart", files={
"reference_photo": ref,
"stego_image": stego,
}, data={
"passphrase": "apple forest thunder mountain",
"pin": "123456"
})
result = response.json()
if result['payload_type'] == 'text':
print(f"Decoded: {result['message']}")
else:
# Save decoded file
file_data = base64.b64decode(result['file_data_base64'])
with open(result['filename'], 'wb') as f:
f.write(file_data)
JavaScript/Node.js
const FormData = require('form-data');
const fs = require('fs');
const axios = require('axios');
const BASE_URL = 'http://localhost:8000';
async function generate() {
const response = await axios.post(`${BASE_URL}/generate`, {
use_pin: true,
words_per_passphrase: 4
});
console.log('Passphrase:', response.data.passphrase);
console.log('PIN:', response.data.pin);
return response.data;
}
async function encode(passphrase, pin) {
const form = new FormData();
form.append('passphrase', passphrase);
form.append('pin', pin);
form.append('message', 'Secret message');
form.append('reference_photo', fs.createReadStream('reference.jpg'));
form.append('carrier', fs.createReadStream('carrier.png'));
const response = await axios.post(`${BASE_URL}/encode/multipart`, form, {
headers: form.getHeaders(),
responseType: 'arraybuffer'
});
fs.writeFileSync('stego.png', response.data);
console.log('Encoded to stego.png');
}
async function decode(passphrase, pin) {
const form = new FormData();
form.append('passphrase', passphrase);
form.append('pin', pin);
form.append('reference_photo', fs.createReadStream('reference.jpg'));
form.append('stego_image', fs.createReadStream('stego.png'));
const response = await axios.post(`${BASE_URL}/decode/multipart`, form, {
headers: form.getHeaders()
});
console.log('Decoded:', response.data.message);
}
// Usage
generate()
.then(creds => encode(creds.passphrase, creds.pin))
.then(() => decode('apple forest thunder mountain', '123456'));
Shell Script (Bash)
#!/bin/bash
BASE_URL="http://localhost:8000"
PASSPHRASE="apple forest thunder mountain"
PIN="123456"
# Generate credentials
echo "Generating credentials..."
CREDS=$(curl -s -X POST "$BASE_URL/generate" \
-H "Content-Type: application/json" \
-d '{"use_pin": true, "words_per_passphrase": 4}')
echo "Passphrase: $(echo $CREDS | jq -r '.passphrase')"
echo "PIN: $(echo $CREDS | jq -r '.pin')"
# Encode
echo "Encoding..."
curl -s -X POST "$BASE_URL/encode/multipart" \
-F "passphrase=$PASSPHRASE" \
-F "pin=$PIN" \
-F "message=Secret message" \
-F "reference_photo=@reference.jpg" \
-F "carrier=@carrier.png" \
--output stego.png
echo "Encoded to stego.png"
# Decode
echo "Decoding..."
RESULT=$(curl -s -X POST "$BASE_URL/decode/multipart" \
-F "passphrase=$PASSPHRASE" \
-F "pin=$PIN" \
-F "reference_photo=@reference.jpg" \
-F "stego_image=@stego.png")
echo "Decoded: $(echo $RESULT | jq -r '.message')"
Rate Limiting
The API does not implement rate limiting by default. For production:
- Reverse Proxy: Use nginx or Caddy rate limiting
- Application Level: Add FastAPI middleware
Example nginx rate limiting:
limit_req_zone $binary_remote_addr zone=stegasoo:10m rate=10r/s;
location /api/ {
limit_req zone=stegasoo burst=20 nodelay;
proxy_pass http://localhost:8000/;
}
Security Considerations
In Transit
- Use HTTPS in production
- Configure TLS at reverse proxy level
Memory Usage
- Argon2id requires 256MB RAM per operation
- DCT mode adds ~100MB for scipy operations
- Concurrent requests can exhaust memory
- Limit workers based on available RAM
Worker calculation:
workers = (available_RAM - 512MB) / 350MB
Input Validation
The API validates:
- PIN format (6-9 digits, no leading zero)
- Passphrase presence
- Image size limits
- RSA key validity
- Embedding mode values
- Output format compatibility
Credential Handling
- Credentials are never logged
- No persistent storage of secrets
- Memory cleared after operations
Interactive Documentation
When the API is running, visit:
- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
See Also
- CLI Documentation - Command-line interface
- Web UI Documentation - Browser interface
- API Update Summary - Migration guide
- README - Project overview