Files
stegasoo/frontends/API.md
2025-12-27 23:57:29 -05:00

20 KiB

Stegasoo REST API Documentation

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 (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.0",
  "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"
}

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

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/png
  • Content-Disposition: attachment; filename=<generated_filename>.png

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"
}

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:

  1. Reverse Proxy: Use nginx or Caddy rate limiting
  2. 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:


See Also