Files
stegasoo/frontends/API.md
2026-01-02 15:45:43 -05:00

20 KiB

Stegasoo REST API Documentation (v4.0.0)

Complete REST API reference for Stegasoo steganography operations.

Table of Contents


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)
  • Channel keys for deployment/group isolation (v4.0.0)
  • 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 v4.0.0

Version 4.0.0 adds channel key support for deployment/group isolation:

Feature Description
Channel keys 256-bit keys that isolate message groups
New endpoints /channel/status, /channel/generate, /channel/set, DELETE /channel
Encode/decode param channel_key parameter on all encode/decode endpoints
Response headers X-Stegasoo-Channel-Mode and X-Stegasoo-Channel-Fingerprint

Key benefits:

  • Isolate messages between teams, deployments, or groups
  • Same credentials can't decode messages from different channels
  • Backward compatible (public mode = no channel key)

Breaking change: v4.0.0 messages (with channel key) cannot be decoded by v3.x installations.


Installation

From PyPI

pip install stegasoo[api]

Running the Server

Development:

cd frontends/api
python main.py

Production:

uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4

Docker with channel key:

STEGASOO_CHANNEL_KEY=XXXX-XXXX-... docker-compose up api

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.

Response

{
  "version": "4.0.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"]
  },
  "channel": {
    "mode": "private",
    "configured": true,
    "fingerprint": "ABCD-••••-••••-••••-••••-••••-••••-3456",
    "source": "~/.stegasoo/channel.key"
  },
  "breaking_changes": {
    "v4_channel_key": "Messages encoded with channel key require same key to decode",
    "format_version": 5,
    "backward_compatible": false
  }
}

GET /modes

Get available embedding modes and channel status.

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",
    "output_formats": ["png", "jpeg"],
    "color_modes": ["grayscale", "color"],
    "capacity_ratio": "~20% of LSB",
    "requires": "scipy"
  },
  "channel": {
    "mode": "private",
    "configured": true,
    "fingerprint": "ABCD-••••-••••-••••-••••-••••-••••-3456"
  }
}

GET /channel/status

Get current channel key status. New in v4.0.0.

Query Parameters

Parameter Type Default Description
reveal boolean false Include full key in response

Response

{
  "mode": "private",
  "configured": true,
  "fingerprint": "ABCD-••••-••••-••••-••••-••••-••••-3456",
  "source": "~/.stegasoo/channel.key",
  "key": null
}

With reveal=true:

{
  "mode": "private",
  "configured": true,
  "fingerprint": "ABCD-••••-••••-••••-••••-••••-••••-3456",
  "source": "~/.stegasoo/channel.key",
  "key": "ABCD-1234-EFGH-5678-IJKL-9012-MNOP-3456"
}

cURL Example

# Show status
curl http://localhost:8000/channel/status

# Reveal full key
curl "http://localhost:8000/channel/status?reveal=true"

POST /channel/generate

Generate a new channel key. New in v4.0.0.

Query Parameters

Parameter Type Default Description
save boolean false Save to user config
save_project boolean false Save to project config

Response

{
  "key": "ABCD-1234-EFGH-5678-IJKL-9012-MNOP-3456",
  "fingerprint": "ABCD-••••-••••-••••-••••-••••-••••-3456",
  "saved": true,
  "save_location": "~/.stegasoo/channel.key"
}

cURL Examples

# Just generate (don't save)
curl -X POST http://localhost:8000/channel/generate

# Generate and save to user config
curl -X POST "http://localhost:8000/channel/generate?save=true"

# Generate and save to project config
curl -X POST "http://localhost:8000/channel/generate?save_project=true"

POST /channel/set

Set/save a channel key to config. New in v4.0.0.

Request Body

{
  "key": "ABCD-1234-EFGH-5678-IJKL-9012-MNOP-3456",
  "location": "user"
}
Field Type Default Description
key string required Channel key
location string "user" "user" or "project"

Response

{
  "success": true,
  "location": "~/.stegasoo/channel.key",
  "fingerprint": "ABCD-••••-••••-••••-••••-••••-••••-3456"
}

DELETE /channel

Clear channel key from config. New in v4.0.0.

Query Parameters

Parameter Type Default Description
location string "user" "user", "project", or "all"

Response

{
  "success": true,
  "mode": "public",
  "still_configured": false,
  "remaining_source": null
}

cURL Example

# Clear user config
curl -X DELETE http://localhost:8000/channel

# Clear project config
curl -X DELETE "http://localhost:8000/channel?location=project"

# Clear all
curl -X DELETE "http://localhost:8000/channel?location=all"

POST /generate

Generate credentials for encoding/decoding.

Request Body

{
  "use_pin": true,
  "use_rsa": false,
  "pin_length": 6,
  "rsa_bits": 2048,
  "words_per_passphrase": 4
}

Response

{
  "passphrase": "abandon ability able about",
  "pin": "847293",
  "rsa_key_pem": null,
  "entropy": {
    "passphrase": 44,
    "pin": 19,
    "rsa": 0,
    "total": 63
  }
}

POST /encode (JSON)

Encode a text message into an image.

Request Body

{
  "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,
  "channel_key": null,
  "embed_mode": "lsb",
  "dct_output_format": "png",
  "dct_color_mode": "grayscale"
}

Channel Key Parameter (v4.0.0)

Value Effect
null Auto mode - use server-configured key
"" (empty string) Public mode - no channel isolation
"XXXX-XXXX-..." Explicit key - use this specific key

Response

{
  "stego_image_base64": "iVBORw0KGgo...",
  "filename": "a1b2c3d4.png",
  "capacity_used_percent": 12.4,
  "embed_mode": "lsb",
  "output_format": "png",
  "color_mode": "color",
  "channel_mode": "private",
  "channel_fingerprint": "ABCD-••••-••••-••••-••••-••••-••••-3456"
}

POST /encode/file

Encode a file into an image (JSON with base64).

Same parameters as /encode, plus:

Field Type Required Description
file_data_base64 string Base64-encoded file data
filename string Original filename
mime_type string MIME type

POST /encode/multipart

Encode using multipart form data (file uploads).

Form Fields

Field Type Required Description
passphrase string Passphrase
reference_photo file Reference photo
carrier file Carrier image
message string * Text message
payload_file file * Binary file to embed
pin string Static PIN
rsa_key file RSA key (.pem)
rsa_key_qr file RSA key (QR code image)
rsa_password string RSA key password
channel_key string "auto" (default), "none"=public, or explicit key
embed_mode string "lsb" or "dct"
dct_output_format string "png" or "jpeg"
dct_color_mode string "grayscale" or "color"

* Provide either message or payload_file

Channel Key in Multipart

For form data, the channel_key field uses strings:

Value Effect
"auto" Use server config (default)
"none" Public mode
"XXXX-XXXX-..." Explicit key

Response

Returns the stego 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-Channel-Mode: private
X-Stegasoo-Channel-Fingerprint: ABCD-••••-...-3456
X-Stegasoo-Version: 4.0.0

<binary image data>

cURL Examples

# Encode with auto channel key (default)
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

# Encode with explicit channel key
curl -X POST http://localhost:8000/encode/multipart \
  -F "passphrase=words here" \
  -F "pin=123456" \
  -F "message=Team message" \
  -F "channel_key=ABCD-1234-EFGH-5678-IJKL-9012-MNOP-3456" \
  -F "reference_photo=@reference.jpg" \
  -F "carrier=@carrier.png" \
  --output stego.png

# Encode in public mode (no channel isolation)
curl -X POST http://localhost:8000/encode/multipart \
  -F "passphrase=words here" \
  -F "pin=123456" \
  -F "message=Public message" \
  -F "channel_key=none" \
  -F "reference_photo=@reference.jpg" \
  -F "carrier=@carrier.png" \
  --output stego.png

POST /decode (JSON)

Decode a message or file from a stego image.

Request Body

{
  "stego_image_base64": "iVBORw0KGgo...",
  "reference_photo_base64": "iVBORw0KGgo...",
  "passphrase": "apple forest thunder mountain",
  "pin": "123456",
  "rsa_key_base64": null,
  "rsa_password": null,
  "channel_key": null,
  "embed_mode": "auto"
}

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": "UEsDBBQAAAA...",
  "filename": "document.pdf",
  "mime_type": "application/pdf"
}

POST /decode/multipart

Decode using multipart form data.

Form Fields

Field Type Required Description
passphrase string Passphrase
reference_photo file Reference photo
stego_image file Stego image to decode
pin string Static PIN
rsa_key file RSA key (.pem)
rsa_key_qr file RSA key (QR code image)
rsa_password string RSA key password
channel_key string "auto" (default), "none"=public, or explicit key
embed_mode string "auto", "lsb", or "dct"

Channel Keys

Overview

Channel keys provide deployment/group isolation. Messages encoded with a channel key can only be decoded with the same key.

Key Format

ABCD-1234-EFGH-5678-IJKL-9012-MNOP-3456
└──┘ └──┘ └──┘ └──┘ └──┘ └──┘ └──┘ └──┘
  8 groups of 4 alphanumeric characters (256 bits)

Storage Locations

Keys are checked in order:

Priority Location Best For
1 STEGASOO_CHANNEL_KEY env var Docker, CI/CD
2 ./config/channel.key Project-specific
3 ~/.stegasoo/channel.key User default

API Parameter Values

JSON Endpoints (/encode, /decode)

Value Effect
null Auto - use server config
"" Public mode
"XXXX-..." Explicit key

Multipart Endpoints (/encode/multipart, /decode/multipart)

Value Effect
"auto" Use server config (default)
"none" Public mode
"XXXX-..." Explicit key

Workflow Example

# 1. Generate a channel key for the team
KEY=$(curl -s -X POST http://localhost:8000/channel/generate | jq -r '.key')
echo "Team key: $KEY"

# 2. Distribute to team members (securely!)

# 3. Each deployment sets the key
export STEGASOO_CHANNEL_KEY=$KEY

# 4. Encode - automatically uses server key
curl -X POST http://localhost:8000/encode/multipart \
  -F "passphrase=team passphrase" \
  -F "pin=123456" \
  -F "message=Team secret" \
  -F "reference_photo=@ref.jpg" \
  -F "carrier=@carrier.png" \
  --output stego.png

# 5. Decode - automatically uses server key
curl -X POST http://localhost:8000/decode/multipart \
  -F "passphrase=team passphrase" \
  -F "pin=123456" \
  -F "reference_photo=@ref.jpg" \
  -F "stego_image=@stego.png"

Data Models

ChannelStatusResponse

{
  "mode": "private",
  "configured": true,
  "fingerprint": "ABCD-••••-...-3456",
  "source": "~/.stegasoo/channel.key",
  "key": "ABCD-1234-..." 
}

EncodeResponse (v4.0.0)

{
  "stego_image_base64": "string",
  "filename": "string",
  "capacity_used_percent": 12.4,
  "embed_mode": "lsb",
  "output_format": "png",
  "color_mode": "color",
  "channel_mode": "private",
  "channel_fingerprint": "ABCD-••••-...-3456"
}

DecodeResponse

{
  "payload_type": "text",
  "message": "string",
  "file_data_base64": null,
  "filename": null,
  "mime_type": null
}

Error Handling

HTTP Status Codes

Code Meaning Use Case
200 OK Successful operation
400 Bad Request Invalid input, capacity error, invalid channel key
401 Unauthorized Decryption failed, channel key mismatch
500 Internal Error Unexpected server error
501 Not Implemented Feature unavailable

Channel Key Errors

Status Error Cause
400 "Invalid channel key format" Key doesn't match XXXX-XXXX-... pattern
401 "Message encoded with channel key but none configured" Need to provide channel key
401 "Message encoded without channel key" Use channel_key="" or "none"

Code Examples

Python

import requests

BASE_URL = "http://localhost:8000"

# Check channel status
status = requests.get(f"{BASE_URL}/channel/status").json()
print(f"Channel mode: {status['mode']}")
print(f"Fingerprint: {status.get('fingerprint', 'N/A')}")

# Generate channel key
response = requests.post(f"{BASE_URL}/channel/generate?save=true")
key_info = response.json()
print(f"Generated: {key_info['fingerprint']}")

# Encode with channel key (auto from server)
with open("ref.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": "Team secret",
        "passphrase": "apple forest thunder",
        "pin": "123456",
        # channel_key defaults to "auto" (use server config)
    })
    
    with open("stego.png", "wb") as f:
        f.write(response.content)
    
    print(f"Channel mode: {response.headers.get('X-Stegasoo-Channel-Mode')}")

# Encode with explicit channel key
with open("ref.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": "Using explicit key",
        "passphrase": "words here",
        "pin": "123456",
        "channel_key": "ABCD-1234-EFGH-5678-IJKL-9012-MNOP-3456",
    })

# Decode
with open("ref.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",
        "pin": "123456",
        # channel_key defaults to "auto"
    })
    
    result = response.json()
    print(f"Decoded: {result.get('message')}")

JavaScript

const axios = require('axios');
const FormData = require('form-data');
const fs = require('fs');

const BASE_URL = 'http://localhost:8000';

async function main() {
    // Check channel status
    const status = await axios.get(`${BASE_URL}/channel/status`);
    console.log('Channel:', status.data.mode);
    
    // Encode with auto channel key
    const form = new FormData();
    form.append('passphrase', 'apple forest thunder');
    form.append('pin', '123456');
    form.append('message', 'Secret');
    form.append('reference_photo', fs.createReadStream('ref.jpg'));
    form.append('carrier', fs.createReadStream('carrier.png'));
    // channel_key defaults to "auto" (use server config)
    
    const response = await axios.post(`${BASE_URL}/encode/multipart`, form, {
        headers: form.getHeaders(),
        responseType: 'arraybuffer'
    });
    
    fs.writeFileSync('stego.png', response.data);
    console.log('Channel mode:', response.headers['x-stegasoo-channel-mode']);
}

main();

cURL / Bash

#!/bin/bash

BASE_URL="http://localhost:8000"

# Check channel status
echo "Channel status:"
curl -s "$BASE_URL/channel/status" | jq .

# Generate and save channel key
echo "Generating channel key..."
curl -s -X POST "$BASE_URL/channel/generate?save=true" | jq .

# Encode (channel_key defaults to "auto")
echo "Encoding..."
curl -s -X POST "$BASE_URL/encode/multipart" \
  -F "passphrase=apple forest thunder" \
  -F "pin=123456" \
  -F "message=Secret message" \
  -F "reference_photo=@ref.jpg" \
  -F "carrier=@carrier.png" \
  --output stego.png

echo "Encoded to stego.png"

# Decode
echo "Decoding..."
curl -s -X POST "$BASE_URL/decode/multipart" \
  -F "passphrase=apple forest thunder" \
  -F "pin=123456" \
  -F "reference_photo=@ref.jpg" \
  -F "stego_image=@stego.png" | jq .

Docker Configuration

docker-compose.yml

x-common-env: &common-env
  STEGASOO_CHANNEL_KEY: ${STEGASOO_CHANNEL_KEY:-}

services:
  api:
    build:
      context: .
      target: api
    ports:
      - "8000:8000"
    environment:
      <<: *common-env

.env (gitignored)

STEGASOO_CHANNEL_KEY=ABCD-1234-EFGH-5678-IJKL-9012-MNOP-3456

Generate key for .env

curl -s -X POST http://localhost:8000/channel/generate | \
  jq -r '"STEGASOO_CHANNEL_KEY=\(.key)"' >> .env

See Also