20 KiB
Stegasoo REST API Documentation
Complete REST API reference for Stegasoo steganography operations.
Table of Contents
- Overview
- Installation
- Authentication
- Base URL
- Endpoints
- Data Models
- Error Handling
- Code Examples
- Rate Limiting
- Security Considerations
Overview
The Stegasoo REST API provides programmatic access to all steganography operations:
- Generate credentials (phrases, PINs, RSA keys)
- Encode messages into images
- Decode messages from images
- Analyze image capacity
The API supports both JSON (base64-encoded images) and multipart form data (direct file uploads).
Installation
From PyPI
pip install stegasoo[api]
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": "2.0.1",
"has_argon2": true,
"day_names": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
}
Response Fields
| Field | Type | Description |
|---|---|---|
version |
string | Stegasoo library version |
has_argon2 |
boolean | Whether Argon2id is available |
day_names |
array | Day names for phrase mapping |
cURL Example
curl http://localhost:8000/
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_phrase": 3
}
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_phrase |
integer | 3 |
Words per phrase (3-12) |
Response
{
"phrases": {
"Monday": "abandon ability able",
"Tuesday": "actor actress actual",
"Wednesday": "advice aerobic affair",
"Thursday": "afraid again age",
"Friday": "agree ahead aim",
"Saturday": "airport aisle alarm",
"Sunday": "album alcohol alert"
},
"pin": "847293",
"rsa_key_pem": null,
"entropy": {
"phrase": 33,
"pin": 19,
"rsa": 0,
"total": 52
}
}
Response Fields
| Field | Type | Description |
|---|---|---|
phrases |
object | Day-to-phrase mapping |
pin |
string|null | Generated PIN (if requested) |
rsa_key_pem |
string|null | PEM-encoded RSA key (if requested) |
entropy.phrase |
integer | Entropy from phrases (bits) |
entropy.pin |
integer | Entropy from PIN (bits) |
entropy.rsa |
integer | Entropy from RSA key (bits) |
entropy.total |
integer | Combined entropy (bits) |
cURL Examples
PIN only:
curl -X POST http://localhost:8000/generate \
-H "Content-Type: application/json" \
-d '{"use_pin": true, "use_rsa": false}'
RSA only:
curl -X POST http://localhost:8000/generate \
-H "Content-Type: application/json" \
-d '{"use_pin": false, "use_rsa": true, "rsa_bits": 4096}'
Both with custom settings:
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_phrase": 6
}'
POST /encode (JSON)
Encode a 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...",
"day_phrase": "apple forest thunder",
"pin": "123456",
"rsa_key_base64": null,
"rsa_password": null,
"date_str": null
}
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
message |
string | ✓ | Message to encode |
reference_photo_base64 |
string | ✓ | Base64-encoded reference photo |
carrier_image_base64 |
string | ✓ | Base64-encoded carrier image |
day_phrase |
string | ✓ | Today's passphrase |
pin |
string | * | Static PIN (6-9 digits) |
rsa_key_base64 |
string | * | Base64-encoded RSA key PEM |
rsa_password |
string | Password for RSA key | |
date_str |
string | Date override (YYYY-MM-DD) |
* At least one of pin or rsa_key_base64 required.
Response
{
"stego_image_base64": "iVBORw0KGgo...",
"filename": "a1b2c3d4_20251227.png",
"capacity_used_percent": 12.4,
"date_used": "2025-12-27",
"day_of_week": "Saturday"
}
Response Fields
| Field | Type | Description |
|---|---|---|
stego_image_base64 |
string | Base64-encoded stego PNG |
filename |
string | Suggested filename |
capacity_used_percent |
float | Percentage of capacity used |
date_used |
string | Date embedded in image (YYYY-MM-DD) |
day_of_week |
string | Day name for passphrase rotation |
cURL Example
# 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\",
\"day_phrase\": \"apple forest thunder\",
\"pin\": \"123456\"
}" | jq -r '.stego_image_base64' | base64 -d > stego.png
POST /encode/multipart
Encode a message using direct file uploads. Returns the stego image directly.
Request
POST /encode/multipart HTTP/1.1
Host: localhost:8000
Content-Type: multipart/form-data; boundary=----FormBoundary
------FormBoundary
Content-Disposition: form-data; name="message"
Secret message here
------FormBoundary
Content-Disposition: form-data; name="day_phrase"
apple forest thunder
------FormBoundary
Content-Disposition: form-data; name="pin"
123456
------FormBoundary
Content-Disposition: form-data; name="reference_photo"; filename="ref.jpg"
Content-Type: image/jpeg
<binary image data>
------FormBoundary
Content-Disposition: form-data; name="carrier"; filename="carrier.png"
Content-Type: image/png
<binary image data>
------FormBoundary--
Form Fields
| Field | Type | Required | Description |
|---|---|---|---|
message |
string | ✓ | Message to encode |
reference_photo |
file | ✓ | Reference photo file |
carrier |
file | ✓ | Carrier image file |
day_phrase |
string | ✓ | Today's passphrase |
pin |
string | * | Static PIN |
rsa_key |
file | * | RSA key file (.pem) |
rsa_password |
string | Password for RSA key | |
date_str |
string | Date override (YYYY-MM-DD) |
* At least one of pin or rsa_key required.
Response
Returns the PNG image directly with headers:
Content-Type: image/pngContent-Disposition: attachment; filename=<generated_filename>.pngX-Stegasoo-Date: 2025-12-27(date used for encoding)X-Stegasoo-Day: Saturday(day of week for passphrase rotation)X-Stegasoo-Capacity-Percent: 12.4(capacity used)
cURL Examples
With PIN:
curl -X POST http://localhost:8000/encode/multipart \
-F "message=Secret message" \
-F "day_phrase=apple forest thunder" \
-F "pin=123456" \
-F "reference_photo=@reference.jpg" \
-F "carrier=@carrier.png" \
--output stego.png
With RSA key:
curl -X POST http://localhost:8000/encode/multipart \
-F "message=Secret message" \
-F "day_phrase=apple forest thunder" \
-F "rsa_key=@mykey.pem" \
-F "rsa_password=keypassword" \
-F "reference_photo=@reference.jpg" \
-F "carrier=@carrier.png" \
--output stego.png
With both PIN and RSA:
curl -X POST http://localhost:8000/encode/multipart \
-F "message=Maximum security message" \
-F "day_phrase=apple forest thunder" \
-F "pin=123456" \
-F "rsa_key=@mykey.pem" \
-F "rsa_password=keypassword" \
-F "reference_photo=@reference.jpg" \
-F "carrier=@carrier.png" \
--output stego.png
With custom date:
curl -X POST http://localhost:8000/encode/multipart \
-F "message=Backdated message" \
-F "day_phrase=monday phrase here" \
-F "pin=123456" \
-F "date_str=2025-12-29" \
-F "reference_photo=@reference.jpg" \
-F "carrier=@carrier.png" \
--output stego.png
POST /decode (JSON)
Decode a message using base64-encoded images.
Request
POST /decode HTTP/1.1
Host: localhost:8000
Content-Type: application/json
{
"stego_image_base64": "iVBORw0KGgo...",
"reference_photo_base64": "iVBORw0KGgo...",
"day_phrase": "apple forest thunder",
"pin": "123456",
"rsa_key_base64": null,
"rsa_password": null
}
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
stego_image_base64 |
string | ✓ | Base64-encoded stego image |
reference_photo_base64 |
string | ✓ | Base64-encoded reference photo |
day_phrase |
string | ✓ | Passphrase for encoding day |
pin |
string | * | Static PIN |
rsa_key_base64 |
string | * | Base64-encoded RSA key |
rsa_password |
string | Password for RSA key |
* Must match the security factors used during encoding.
Response
{
"message": "Secret message here"
}
cURL Example
# Prepare base64-encoded images
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\",
\"day_phrase\": \"apple forest thunder\",
\"pin\": \"123456\"
}"
POST /decode/multipart
Decode a message using direct file uploads.
Request
POST /decode/multipart HTTP/1.1
Host: localhost:8000
Content-Type: multipart/form-data
Form Fields
| Field | Type | Required | Description |
|---|---|---|---|
stego_image |
file | ✓ | Stego image file |
reference_photo |
file | ✓ | Reference photo file |
day_phrase |
string | ✓ | Passphrase for encoding day |
pin |
string | * | Static PIN |
rsa_key |
file | * | RSA key file |
rsa_password |
string | Password for RSA key |
Response
{
"message": "Secret message here"
}
cURL Examples
With PIN:
curl -X POST http://localhost:8000/decode/multipart \
-F "day_phrase=apple forest thunder" \
-F "pin=123456" \
-F "reference_photo=@reference.jpg" \
-F "stego_image=@stego.png"
With RSA key:
curl -X POST http://localhost:8000/decode/multipart \
-F "day_phrase=apple forest thunder" \
-F "rsa_key=@mykey.pem" \
-F "rsa_password=keypassword" \
-F "reference_photo=@reference.jpg" \
-F "stego_image=@stego.png"
POST /image/info
Get information about an image's capacity.
Request
POST /image/info HTTP/1.1
Host: localhost:8000
Content-Type: multipart/form-data
Form Fields
| Field | Type | Required | Description |
|---|---|---|---|
image |
file | ✓ | Image file to analyze |
Response
{
"width": 1920,
"height": 1080,
"pixels": 2073600,
"capacity_bytes": 776970,
"capacity_kb": 758
}
Response Fields
| Field | Type | Description |
|---|---|---|
width |
integer | Image width in pixels |
height |
integer | Image height in pixels |
pixels |
integer | Total pixel count |
capacity_bytes |
integer | Maximum message capacity (bytes) |
capacity_kb |
integer | Maximum message capacity (KB) |
cURL Example
curl -X POST http://localhost:8000/image/info \
-F "image=@myimage.png"
Data Models
GenerateRequest
{
"use_pin": true,
"use_rsa": false,
"pin_length": 6,
"rsa_bits": 2048,
"words_per_phrase": 3
}
GenerateResponse
{
"phrases": {"Monday": "...", "Tuesday": "...", ...},
"pin": "123456",
"rsa_key_pem": "-----BEGIN PRIVATE KEY-----...",
"entropy": {"phrase": 33, "pin": 19, "rsa": 0, "total": 52}
}
EncodeRequest
{
"message": "string",
"reference_photo_base64": "string",
"carrier_image_base64": "string",
"day_phrase": "string",
"pin": "string",
"rsa_key_base64": "string",
"rsa_password": "string",
"date_str": "YYYY-MM-DD"
}
EncodeResponse
{
"stego_image_base64": "string",
"filename": "string",
"capacity_used_percent": 12.4,
"date_used": "YYYY-MM-DD",
"day_of_week": "Saturday"
}
DecodeRequest
{
"stego_image_base64": "string",
"reference_photo_base64": "string",
"day_phrase": "string",
"pin": "string",
"rsa_key_base64": "string",
"rsa_password": "string"
}
DecodeResponse
{
"message": "string"
}
ImageInfoResponse
{
"width": 1920,
"height": 1080,
"pixels": 2073600,
"capacity_bytes": 776970,
"capacity_kb": 758
}
ErrorResponse
{
"error": "ErrorType",
"detail": "Error description"
}
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 |
Error Response Format
{
"detail": "Error message describing the problem"
}
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 | "rsa_bits must be one of [2048, 3072, 4096]" | Use valid RSA key size |
| 400 | "Carrier image too small" | Use larger carrier image |
| 400 | "PIN must be 6-9 digits" | Fix PIN format |
| 401 | "Decryption failed. Check credentials." | Verify phrase, PIN, ref photo |
| 400 | "Message too long" | Reduce message size or use larger carrier |
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,
"use_rsa": False,
"words_per_phrase": 3
})
creds = response.json()
print(f"PIN: {creds['pin']}")
print(f"Monday phrase: {creds['phrases']['Monday']}")
# Encode using multipart
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",
"day_phrase": "apple forest thunder",
"pin": "123456"
})
with open("stego.png", "wb") as f:
f.write(response.content)
# Decode using multipart
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={
"day_phrase": "apple forest thunder",
"pin": "123456"
})
print(f"Decoded: {response.json()['message']}")
JavaScript/Node.js
const FormData = require('form-data');
const fs = require('fs');
const axios = require('axios');
const BASE_URL = 'http://localhost:8000';
async function encode() {
const form = new FormData();
form.append('message', 'Secret message');
form.append('day_phrase', 'apple forest thunder');
form.append('pin', '123456');
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 successfully');
}
async function decode() {
const form = new FormData();
form.append('day_phrase', 'apple forest thunder');
form.append('pin', '123456');
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);
}
encode().then(decode);
Go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
)
func main() {
// Encode
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
writer.WriteField("message", "Secret message")
writer.WriteField("day_phrase", "apple forest thunder")
writer.WriteField("pin", "123456")
ref, _ := os.Open("reference.jpg")
refPart, _ := writer.CreateFormFile("reference_photo", "reference.jpg")
io.Copy(refPart, ref)
ref.Close()
carrier, _ := os.Open("carrier.png")
carrierPart, _ := writer.CreateFormFile("carrier", "carrier.png")
io.Copy(carrierPart, carrier)
carrier.Close()
writer.Close()
resp, _ := http.Post(
"http://localhost:8000/encode/multipart",
writer.FormDataContentType(),
body,
)
stego, _ := os.Create("stego.png")
io.Copy(stego, resp.Body)
stego.Close()
resp.Body.Close()
fmt.Println("Encoded successfully")
}
Shell Script (Bash)
#!/bin/bash
BASE_URL="http://localhost:8000"
REF_PHOTO="reference.jpg"
CARRIER="carrier.png"
PHRASE="apple forest thunder"
PIN="123456"
MESSAGE="Secret message"
# Encode
echo "Encoding..."
curl -s -X POST "$BASE_URL/encode/multipart" \
-F "message=$MESSAGE" \
-F "day_phrase=$PHRASE" \
-F "pin=$PIN" \
-F "reference_photo=@$REF_PHOTO" \
-F "carrier=@$CARRIER" \
--output stego.png
echo "Encoded to stego.png"
# Decode
echo "Decoding..."
DECODED=$(curl -s -X POST "$BASE_URL/decode/multipart" \
-F "day_phrase=$PHRASE" \
-F "pin=$PIN" \
-F "reference_photo=@$REF_PHOTO" \
-F "stego_image=@stego.png" | jq -r '.message')
echo "Decoded message: $DECODED"
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
- Concurrent requests can exhaust memory
- Limit workers based on available RAM
Input Validation
The API validates:
- PIN format (6-9 digits, no leading zero)
- Message size (max 50KB)
- Image size (max 5MB file, ~4MP dimensions)
- RSA key validity
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
- README - Project overview