Clean up tasks homie.
This commit is contained in:
130
Dockerfile.txt
130
Dockerfile.txt
@@ -1,130 +0,0 @@
|
|||||||
# Stegasoo Docker Image
|
|
||||||
# Multi-stage build for smaller image size
|
|
||||||
|
|
||||||
FROM python:3.11-slim as base
|
|
||||||
|
|
||||||
# Set environment variables
|
|
||||||
ENV PYTHONDONTWRITEBYTECODE=1
|
|
||||||
ENV PYTHONUNBUFFERED=1
|
|
||||||
|
|
||||||
# Install system dependencies
|
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
||||||
gcc \
|
|
||||||
libc-dev \
|
|
||||||
libffi-dev \
|
|
||||||
libzbar0 \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# Builder stage - install Python packages
|
|
||||||
# ============================================================================
|
|
||||||
FROM base as builder
|
|
||||||
|
|
||||||
WORKDIR /build
|
|
||||||
|
|
||||||
# Copy package files (including README.md which pyproject.toml references)
|
|
||||||
COPY pyproject.toml README.md ./
|
|
||||||
COPY src/ src/
|
|
||||||
COPY data/ data/
|
|
||||||
|
|
||||||
# Install the package with web extras
|
|
||||||
RUN pip install --no-cache-dir ".[web]"
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# Production stage - Web UI
|
|
||||||
# ============================================================================
|
|
||||||
FROM base as web
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Copy installed packages from builder
|
|
||||||
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
|
|
||||||
COPY --from=builder /usr/local/bin /usr/local/bin
|
|
||||||
|
|
||||||
# Copy application files
|
|
||||||
COPY src/ src/
|
|
||||||
COPY data/ data/
|
|
||||||
COPY frontends/web/ frontends/web/
|
|
||||||
|
|
||||||
# Create upload directory
|
|
||||||
RUN mkdir -p /tmp/stego_uploads
|
|
||||||
|
|
||||||
# Create non-root user
|
|
||||||
RUN useradd -m -u 1000 stego && chown -R stego:stego /app /tmp/stego_uploads
|
|
||||||
USER stego
|
|
||||||
|
|
||||||
# Set Python path
|
|
||||||
ENV PYTHONPATH=/app/src
|
|
||||||
|
|
||||||
# Expose port
|
|
||||||
EXPOSE 5000
|
|
||||||
|
|
||||||
# Health check
|
|
||||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
|
||||||
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:5000/')" || exit 1
|
|
||||||
|
|
||||||
# Run with gunicorn
|
|
||||||
WORKDIR /app/frontends/web
|
|
||||||
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "2", "--threads", "4", "--timeout", "60", "app:app"]
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# API stage - REST API
|
|
||||||
# ============================================================================
|
|
||||||
FROM base as api
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Install API extras
|
|
||||||
COPY pyproject.toml README.md ./
|
|
||||||
COPY src/ src/
|
|
||||||
COPY data/ data/
|
|
||||||
RUN pip install --no-cache-dir ".[api]"
|
|
||||||
|
|
||||||
# Copy API files
|
|
||||||
COPY frontends/api/ frontends/api/
|
|
||||||
|
|
||||||
# Create non-root user
|
|
||||||
RUN useradd -m -u 1000 stego && chown -R stego:stego /app
|
|
||||||
USER stego
|
|
||||||
|
|
||||||
# Set Python path
|
|
||||||
ENV PYTHONPATH=/app/src
|
|
||||||
|
|
||||||
# Expose port
|
|
||||||
EXPOSE 8000
|
|
||||||
|
|
||||||
# Health check
|
|
||||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
|
||||||
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/')" || exit 1
|
|
||||||
|
|
||||||
# Run with uvicorn
|
|
||||||
WORKDIR /app/frontends/api
|
|
||||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# CLI stage - Command line tool
|
|
||||||
# ============================================================================
|
|
||||||
FROM base as cli
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Install CLI extras
|
|
||||||
COPY pyproject.toml README.md ./
|
|
||||||
COPY src/ src/
|
|
||||||
COPY data/ data/
|
|
||||||
RUN pip install --no-cache-dir ".[cli]"
|
|
||||||
|
|
||||||
# Copy CLI files
|
|
||||||
COPY frontends/cli/ frontends/cli/
|
|
||||||
|
|
||||||
# Create non-root user
|
|
||||||
RUN useradd -m -u 1000 stego && chown -R stego:stego /app
|
|
||||||
USER stego
|
|
||||||
|
|
||||||
# Set Python path
|
|
||||||
ENV PYTHONPATH=/app/src
|
|
||||||
|
|
||||||
# Default to help
|
|
||||||
WORKDIR /app/frontends/cli
|
|
||||||
ENTRYPOINT ["python", "main.py"]
|
|
||||||
CMD ["--help"]
|
|
||||||
130
Dockerfile_old
130
Dockerfile_old
@@ -1,130 +0,0 @@
|
|||||||
# Stegasoo Docker Image
|
|
||||||
# Multi-stage build for smaller image size
|
|
||||||
|
|
||||||
FROM python:3.11-slim as base
|
|
||||||
|
|
||||||
# Set environment variables
|
|
||||||
ENV PYTHONDONTWRITEBYTECODE=1
|
|
||||||
ENV PYTHONUNBUFFERED=1
|
|
||||||
|
|
||||||
# Install system dependencies
|
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
||||||
gcc \
|
|
||||||
libc-dev \
|
|
||||||
libffi-dev \
|
|
||||||
libzbar0 \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# Builder stage - install Python packages
|
|
||||||
# ============================================================================
|
|
||||||
FROM base as builder
|
|
||||||
|
|
||||||
WORKDIR /build
|
|
||||||
|
|
||||||
# Copy package files (including README.md which pyproject.toml references)
|
|
||||||
COPY pyproject.toml README.md ./
|
|
||||||
COPY src/ src/
|
|
||||||
COPY data/ data/
|
|
||||||
|
|
||||||
# Install the package with web extras
|
|
||||||
RUN pip install --no-cache-dir ".[web]"
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# Production stage - Web UI
|
|
||||||
# ============================================================================
|
|
||||||
FROM base as web
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Copy installed packages from builder
|
|
||||||
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
|
|
||||||
COPY --from=builder /usr/local/bin /usr/local/bin
|
|
||||||
|
|
||||||
# Copy application files
|
|
||||||
COPY src/ src/
|
|
||||||
COPY data/ data/
|
|
||||||
COPY frontends/web/ frontends/web/
|
|
||||||
|
|
||||||
# Create upload directory
|
|
||||||
RUN mkdir -p /tmp/stego_uploads
|
|
||||||
|
|
||||||
# Create non-root user
|
|
||||||
RUN useradd -m -u 1000 stego && chown -R stego:stego /app /tmp/stego_uploads
|
|
||||||
USER stego
|
|
||||||
|
|
||||||
# Set Python path
|
|
||||||
ENV PYTHONPATH=/app/src
|
|
||||||
|
|
||||||
# Expose port
|
|
||||||
EXPOSE 5000
|
|
||||||
|
|
||||||
# Health check
|
|
||||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
|
||||||
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:5000/')" || exit 1
|
|
||||||
|
|
||||||
# Run with gunicorn
|
|
||||||
WORKDIR /app/frontends/web
|
|
||||||
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "2", "--threads", "4", "--timeout", "60", "app:app"]
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# API stage - REST API
|
|
||||||
# ============================================================================
|
|
||||||
FROM base as api
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Install API extras
|
|
||||||
COPY pyproject.toml README.md ./
|
|
||||||
COPY src/ src/
|
|
||||||
COPY data/ data/
|
|
||||||
RUN pip install --no-cache-dir ".[api]"
|
|
||||||
|
|
||||||
# Copy API files
|
|
||||||
COPY frontends/api/ frontends/api/
|
|
||||||
|
|
||||||
# Create non-root user
|
|
||||||
RUN useradd -m -u 1000 stego && chown -R stego:stego /app
|
|
||||||
USER stego
|
|
||||||
|
|
||||||
# Set Python path
|
|
||||||
ENV PYTHONPATH=/app/src
|
|
||||||
|
|
||||||
# Expose port
|
|
||||||
EXPOSE 8000
|
|
||||||
|
|
||||||
# Health check
|
|
||||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
|
||||||
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/')" || exit 1
|
|
||||||
|
|
||||||
# Run with uvicorn
|
|
||||||
WORKDIR /app/frontends/api
|
|
||||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# CLI stage - Command line tool
|
|
||||||
# ============================================================================
|
|
||||||
FROM base as cli
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Install CLI extras
|
|
||||||
COPY pyproject.toml README.md ./
|
|
||||||
COPY src/ src/
|
|
||||||
COPY data/ data/
|
|
||||||
RUN pip install --no-cache-dir ".[cli]"
|
|
||||||
|
|
||||||
# Copy CLI files
|
|
||||||
COPY frontends/cli/ frontends/cli/
|
|
||||||
|
|
||||||
# Create non-root user
|
|
||||||
RUN useradd -m -u 1000 stego && chown -R stego:stego /app
|
|
||||||
USER stego
|
|
||||||
|
|
||||||
# Set Python path
|
|
||||||
ENV PYTHONPATH=/app/src
|
|
||||||
|
|
||||||
# Default to help
|
|
||||||
WORKDIR /app/frontends/cli
|
|
||||||
ENTRYPOINT ["python", "main.py"]
|
|
||||||
CMD ["--help"]
|
|
||||||
621
init_head.py
621
init_head.py
@@ -1,621 +0,0 @@
|
|||||||
"""
|
|
||||||
Stegasoo - Secure Steganography Library
|
|
||||||
|
|
||||||
A Python library for hiding encrypted messages and files in images using
|
|
||||||
hybrid photo + passphrase + PIN authentication.
|
|
||||||
|
|
||||||
Basic Usage - Text Message:
|
|
||||||
from stegasoo import encode, decode, generate_credentials
|
|
||||||
|
|
||||||
# Generate credentials
|
|
||||||
creds = generate_credentials(use_pin=True, use_rsa=False)
|
|
||||||
print(creds.phrases['Monday'])
|
|
||||||
print(creds.pin)
|
|
||||||
|
|
||||||
# Encode a message
|
|
||||||
with open('secret.jpg', 'rb') as f:
|
|
||||||
ref_photo = f.read()
|
|
||||||
with open('meme.png', 'rb') as f:
|
|
||||||
carrier = f.read()
|
|
||||||
|
|
||||||
result = encode(
|
|
||||||
message="Meet at midnight",
|
|
||||||
reference_photo=ref_photo,
|
|
||||||
carrier_image=carrier,
|
|
||||||
day_phrase="apple forest thunder",
|
|
||||||
pin="123456"
|
|
||||||
)
|
|
||||||
|
|
||||||
with open('stego.png', 'wb') as f:
|
|
||||||
f.write(result.stego_image)
|
|
||||||
|
|
||||||
# Decode a message
|
|
||||||
decoded = decode(
|
|
||||||
stego_image=result.stego_image,
|
|
||||||
reference_photo=ref_photo,
|
|
||||||
day_phrase="apple forest thunder",
|
|
||||||
pin="123456"
|
|
||||||
)
|
|
||||||
print(decoded.message) # "Meet at midnight"
|
|
||||||
|
|
||||||
File Embedding:
|
|
||||||
from stegasoo import encode_file, decode, FilePayload
|
|
||||||
|
|
||||||
# Encode a file
|
|
||||||
result = encode_file(
|
|
||||||
filepath="secret_document.pdf",
|
|
||||||
reference_photo=ref_photo,
|
|
||||||
carrier_image=carrier,
|
|
||||||
day_phrase="apple forest thunder",
|
|
||||||
pin="123456"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Decode - automatically detects file vs text
|
|
||||||
decoded = decode(...)
|
|
||||||
if decoded.is_file:
|
|
||||||
with open(decoded.filename, 'wb') as f:
|
|
||||||
f.write(decoded.file_data)
|
|
||||||
else:
|
|
||||||
print(decoded.message)
|
|
||||||
|
|
||||||
Debugging:
|
|
||||||
from stegasoo.debug import debug
|
|
||||||
debug.enable(True) # Enable debug output
|
|
||||||
debug.enable_performance(True) # Enable timing
|
|
||||||
"""
|
|
||||||
|
|
||||||
from .constants import __version__, DAY_NAMES, MAX_MESSAGE_SIZE, MAX_FILE_PAYLOAD_SIZE
|
|
||||||
from .models import (
|
|
||||||
Credentials,
|
|
||||||
EncodeInput,
|
|
||||||
EncodeResult,
|
|
||||||
DecodeInput,
|
|
||||||
DecodeResult,
|
|
||||||
EmbedStats,
|
|
||||||
KeyInfo,
|
|
||||||
ValidationResult,
|
|
||||||
FilePayload,
|
|
||||||
)
|
|
||||||
from .exceptions import (
|
|
||||||
StegasooError,
|
|
||||||
ValidationError,
|
|
||||||
PinValidationError,
|
|
||||||
MessageValidationError,
|
|
||||||
ImageValidationError,
|
|
||||||
KeyValidationError,
|
|
||||||
SecurityFactorError,
|
|
||||||
CryptoError,
|
|
||||||
EncryptionError,
|
|
||||||
DecryptionError,
|
|
||||||
KeyDerivationError,
|
|
||||||
KeyGenerationError,
|
|
||||||
KeyPasswordError,
|
|
||||||
SteganographyError,
|
|
||||||
CapacityError,
|
|
||||||
ExtractionError,
|
|
||||||
EmbeddingError,
|
|
||||||
InvalidHeaderError,
|
|
||||||
)
|
|
||||||
from .keygen import (
|
|
||||||
generate_credentials,
|
|
||||||
generate_pin,
|
|
||||||
generate_phrase,
|
|
||||||
generate_day_phrases,
|
|
||||||
generate_rsa_key,
|
|
||||||
export_rsa_key_pem,
|
|
||||||
load_rsa_key,
|
|
||||||
get_key_info,
|
|
||||||
)
|
|
||||||
from .validation import (
|
|
||||||
validate_pin,
|
|
||||||
validate_message,
|
|
||||||
validate_payload,
|
|
||||||
validate_file_payload,
|
|
||||||
validate_image,
|
|
||||||
validate_rsa_key,
|
|
||||||
validate_security_factors,
|
|
||||||
validate_phrase,
|
|
||||||
validate_date_string,
|
|
||||||
require_valid_pin,
|
|
||||||
require_valid_message,
|
|
||||||
require_valid_payload,
|
|
||||||
require_valid_image,
|
|
||||||
require_valid_rsa_key,
|
|
||||||
require_security_factors,
|
|
||||||
)
|
|
||||||
from .crypto import (
|
|
||||||
encrypt_message,
|
|
||||||
decrypt_message,
|
|
||||||
decrypt_message_text,
|
|
||||||
derive_hybrid_key,
|
|
||||||
derive_pixel_key,
|
|
||||||
hash_photo,
|
|
||||||
parse_header,
|
|
||||||
get_date_from_encrypted,
|
|
||||||
has_argon2,
|
|
||||||
)
|
|
||||||
from .steganography import (
|
|
||||||
embed_in_image,
|
|
||||||
extract_from_image,
|
|
||||||
calculate_capacity,
|
|
||||||
get_image_dimensions,
|
|
||||||
get_image_format,
|
|
||||||
is_lossless_format,
|
|
||||||
LOSSLESS_FORMATS,
|
|
||||||
)
|
|
||||||
from .utils import (
|
|
||||||
generate_filename,
|
|
||||||
parse_date_from_filename,
|
|
||||||
get_day_from_date,
|
|
||||||
get_today_date,
|
|
||||||
get_today_day,
|
|
||||||
secure_delete,
|
|
||||||
SecureDeleter,
|
|
||||||
format_file_size,
|
|
||||||
)
|
|
||||||
from .debug import debug # Import debug utilities
|
|
||||||
|
|
||||||
# QR Code utilities (optional, depends on qrcode and pyzbar)
|
|
||||||
try:
|
|
||||||
from .qr_utils import (
|
|
||||||
generate_qr_code,
|
|
||||||
read_qr_code,
|
|
||||||
read_qr_code_from_file,
|
|
||||||
extract_key_from_qr,
|
|
||||||
extract_key_from_qr_file,
|
|
||||||
compress_data,
|
|
||||||
decompress_data,
|
|
||||||
auto_decompress,
|
|
||||||
normalize_pem,
|
|
||||||
is_compressed,
|
|
||||||
can_fit_in_qr,
|
|
||||||
needs_compression,
|
|
||||||
has_qr_read,
|
|
||||||
has_qr_write,
|
|
||||||
has_qr_support,
|
|
||||||
)
|
|
||||||
HAS_QR_UTILS = True
|
|
||||||
except ImportError:
|
|
||||||
HAS_QR_UTILS = False
|
|
||||||
|
|
||||||
from datetime import date
|
|
||||||
from pathlib import Path
|
|
||||||
from typing import Optional, Union, Dict, Any
|
|
||||||
|
|
||||||
|
|
||||||
def encode(
|
|
||||||
message: Union[str, bytes, FilePayload],
|
|
||||||
reference_photo: bytes,
|
|
||||||
carrier_image: bytes,
|
|
||||||
day_phrase: str,
|
|
||||||
pin: str = "",
|
|
||||||
rsa_key_data: Optional[bytes] = None,
|
|
||||||
rsa_password: Optional[str] = None,
|
|
||||||
date_str: Optional[str] = None,
|
|
||||||
output_format: Optional[str] = None,
|
|
||||||
) -> EncodeResult:
|
|
||||||
"""
|
|
||||||
Encode a secret message or file into an image.
|
|
||||||
|
|
||||||
High-level convenience function that handles validation,
|
|
||||||
encryption, and embedding in one call.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
message: Secret message (str), raw bytes, or FilePayload to hide
|
|
||||||
reference_photo: Shared reference photo bytes
|
|
||||||
carrier_image: Image to hide message in
|
|
||||||
day_phrase: Today's passphrase
|
|
||||||
pin: Static PIN (optional if using RSA key)
|
|
||||||
rsa_key_data: RSA private key PEM bytes (optional if using PIN)
|
|
||||||
rsa_password: Password for RSA key if encrypted
|
|
||||||
date_str: Date string YYYY-MM-DD (defaults to today)
|
|
||||||
output_format: Force output format ('PNG', 'BMP'). If None, preserves
|
|
||||||
carrier format for lossless types, defaults to PNG for lossy.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
EncodeResult with stego image and metadata
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
ValidationError: If inputs are invalid
|
|
||||||
SecurityFactorError: If no PIN or RSA key provided
|
|
||||||
CapacityError: If carrier is too small
|
|
||||||
EncryptionError: If encryption fails
|
|
||||||
|
|
||||||
Note:
|
|
||||||
Output format is always lossless (PNG or BMP) to preserve hidden data.
|
|
||||||
If carrier is JPEG/GIF, output will be PNG to maintain data integrity.
|
|
||||||
"""
|
|
||||||
# Debug logging
|
|
||||||
debug.print(f"encode called: message type={type(message).__name__}, "
|
|
||||||
f"day_phrase='{day_phrase[:20]}...', pin_length={len(pin)}")
|
|
||||||
|
|
||||||
# Validate inputs
|
|
||||||
require_valid_payload(message)
|
|
||||||
require_valid_image(carrier_image, "Carrier image")
|
|
||||||
require_security_factors(pin, rsa_key_data)
|
|
||||||
|
|
||||||
if pin:
|
|
||||||
require_valid_pin(pin)
|
|
||||||
if rsa_key_data:
|
|
||||||
require_valid_rsa_key(rsa_key_data, rsa_password)
|
|
||||||
|
|
||||||
# Default date to today
|
|
||||||
if date_str is None:
|
|
||||||
date_str = date.today().isoformat()
|
|
||||||
|
|
||||||
debug.print(f"Encoding for date: {date_str}")
|
|
||||||
|
|
||||||
# Encrypt message/file
|
|
||||||
encrypted = encrypt_message(
|
|
||||||
message, reference_photo, day_phrase, date_str, pin, rsa_key_data
|
|
||||||
)
|
|
||||||
|
|
||||||
# Debug: show encrypted data size
|
|
||||||
debug.print(f"Encrypted payload: {len(encrypted)} bytes")
|
|
||||||
|
|
||||||
# Get pixel key
|
|
||||||
pixel_key = derive_pixel_key(
|
|
||||||
reference_photo, day_phrase, date_str, pin, rsa_key_data
|
|
||||||
)
|
|
||||||
|
|
||||||
debug.data(pixel_key, "Pixel key")
|
|
||||||
|
|
||||||
# Embed in image (returns extension too)
|
|
||||||
stego_data, stats, extension = embed_in_image(
|
|
||||||
carrier_image, encrypted, pixel_key, output_format=output_format
|
|
||||||
)
|
|
||||||
|
|
||||||
# Generate filename with correct extension
|
|
||||||
filename = generate_filename(date_str, extension=extension)
|
|
||||||
|
|
||||||
debug.print(f"Encoding complete: {filename}, "
|
|
||||||
f"modified {stats.pixels_modified}/{stats.total_pixels} pixels "
|
|
||||||
f"({stats.modification_percent:.2f}%)")
|
|
||||||
|
|
||||||
return EncodeResult(
|
|
||||||
stego_image=stego_data,
|
|
||||||
filename=filename,
|
|
||||||
pixels_modified=stats.pixels_modified,
|
|
||||||
total_pixels=stats.total_pixels,
|
|
||||||
capacity_used=stats.capacity_used,
|
|
||||||
date_used=date_str
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def encode_file(
|
|
||||||
filepath: Union[str, Path],
|
|
||||||
reference_photo: bytes,
|
|
||||||
carrier_image: bytes,
|
|
||||||
day_phrase: str,
|
|
||||||
pin: str = "",
|
|
||||||
rsa_key_data: Optional[bytes] = None,
|
|
||||||
rsa_password: Optional[str] = None,
|
|
||||||
date_str: Optional[str] = None,
|
|
||||||
output_format: Optional[str] = None,
|
|
||||||
filename_override: Optional[str] = None,
|
|
||||||
) -> EncodeResult:
|
|
||||||
"""
|
|
||||||
Encode a file into an image.
|
|
||||||
|
|
||||||
Convenience function for embedding files. Preserves original filename.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
filepath: Path to file to embed
|
|
||||||
reference_photo: Shared reference photo bytes
|
|
||||||
carrier_image: Image to hide file in
|
|
||||||
day_phrase: Today's passphrase
|
|
||||||
pin: Static PIN (optional if using RSA key)
|
|
||||||
rsa_key_data: RSA private key PEM bytes (optional if using PIN)
|
|
||||||
rsa_password: Password for RSA key if encrypted
|
|
||||||
date_str: Date string YYYY-MM-DD (defaults to today)
|
|
||||||
output_format: Force output format ('PNG', 'BMP')
|
|
||||||
filename_override: Override the stored filename
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
EncodeResult with stego image and metadata
|
|
||||||
"""
|
|
||||||
debug.print(f"encode_file called: filepath={filepath}")
|
|
||||||
payload = FilePayload.from_file(str(filepath), filename_override)
|
|
||||||
|
|
||||||
return encode(
|
|
||||||
message=payload,
|
|
||||||
reference_photo=reference_photo,
|
|
||||||
carrier_image=carrier_image,
|
|
||||||
day_phrase=day_phrase,
|
|
||||||
pin=pin,
|
|
||||||
rsa_key_data=rsa_key_data,
|
|
||||||
rsa_password=rsa_password,
|
|
||||||
date_str=date_str,
|
|
||||||
output_format=output_format,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def encode_bytes(
|
|
||||||
data: bytes,
|
|
||||||
filename: str,
|
|
||||||
reference_photo: bytes,
|
|
||||||
carrier_image: bytes,
|
|
||||||
day_phrase: str,
|
|
||||||
pin: str = "",
|
|
||||||
rsa_key_data: Optional[bytes] = None,
|
|
||||||
rsa_password: Optional[str] = None,
|
|
||||||
date_str: Optional[str] = None,
|
|
||||||
output_format: Optional[str] = None,
|
|
||||||
mime_type: Optional[str] = None,
|
|
||||||
) -> EncodeResult:
|
|
||||||
"""
|
|
||||||
Encode raw bytes with a filename into an image.
|
|
||||||
|
|
||||||
Convenience function for embedding binary data with metadata.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
data: Raw bytes to embed
|
|
||||||
filename: Filename to associate with the data
|
|
||||||
reference_photo: Shared reference photo bytes
|
|
||||||
carrier_image: Image to hide data in
|
|
||||||
day_phrase: Today's passphrase
|
|
||||||
pin: Static PIN (optional if using RSA key)
|
|
||||||
rsa_key_data: RSA private key PEM bytes (optional if using PIN)
|
|
||||||
rsa_password: Password for RSA key if encrypted
|
|
||||||
date_str: Date string YYYY-MM-DD (defaults to today)
|
|
||||||
output_format: Force output format ('PNG', 'BMP')
|
|
||||||
mime_type: MIME type of the data
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
EncodeResult with stego image and metadata
|
|
||||||
"""
|
|
||||||
debug.print(f"encode_bytes called: filename={filename}, data_size={len(data)}")
|
|
||||||
payload = FilePayload(data=data, filename=filename, mime_type=mime_type)
|
|
||||||
|
|
||||||
return encode(
|
|
||||||
message=payload,
|
|
||||||
reference_photo=reference_photo,
|
|
||||||
carrier_image=carrier_image,
|
|
||||||
day_phrase=day_phrase,
|
|
||||||
pin=pin,
|
|
||||||
rsa_key_data=rsa_key_data,
|
|
||||||
rsa_password=rsa_password,
|
|
||||||
date_str=date_str,
|
|
||||||
output_format=output_format,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@debug.time
|
|
||||||
def decode(
|
|
||||||
stego_image: bytes,
|
|
||||||
reference_photo: bytes,
|
|
||||||
day_phrase: str,
|
|
||||||
pin: str = "",
|
|
||||||
rsa_key_data: Optional[bytes] = None,
|
|
||||||
rsa_password: Optional[str] = None,
|
|
||||||
date_str: Optional[str] = None,
|
|
||||||
) -> DecodeResult:
|
|
||||||
"""
|
|
||||||
Decode a secret message or file from a stego image.
|
|
||||||
|
|
||||||
High-level convenience function that handles extraction
|
|
||||||
and decryption in one call.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
stego_image: Image containing hidden message/file
|
|
||||||
reference_photo: Shared reference photo bytes
|
|
||||||
day_phrase: Passphrase for the day message was encoded
|
|
||||||
pin: Static PIN (if used during encoding)
|
|
||||||
rsa_key_data: RSA private key PEM bytes (if used during encoding)
|
|
||||||
rsa_password: Password for RSA key if encrypted
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
DecodeResult with:
|
|
||||||
- .payload_type: 'text' or 'file'
|
|
||||||
- .message: Decoded text (if text)
|
|
||||||
- .file_data: Decoded bytes (if file)
|
|
||||||
- .filename: Original filename (if file)
|
|
||||||
- .is_text / .is_file: Convenience properties
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
ValidationError: If inputs are invalid
|
|
||||||
SecurityFactorError: If no PIN or RSA key provided
|
|
||||||
ExtractionError: If data cannot be extracted
|
|
||||||
DecryptionError: If decryption fails
|
|
||||||
"""
|
|
||||||
debug.print(f"decode called: stego_image_size={len(stego_image)}, "
|
|
||||||
f"day_phrase='{day_phrase[:20]}...'")
|
|
||||||
|
|
||||||
# Validate inputs
|
|
||||||
require_security_factors(pin, rsa_key_data)
|
|
||||||
|
|
||||||
if pin:
|
|
||||||
require_valid_pin(pin)
|
|
||||||
if rsa_key_data:
|
|
||||||
require_valid_rsa_key(rsa_key_data, rsa_password)
|
|
||||||
|
|
||||||
# Try to extract with today's date first
|
|
||||||
# Use provided date or fall back to today
|
|
||||||
if date_str is None:
|
|
||||||
date_str = date.today().isoformat()
|
|
||||||
pixel_key = derive_pixel_key(
|
|
||||||
reference_photo, day_phrase, date_str, pin, rsa_key_data
|
|
||||||
)
|
|
||||||
|
|
||||||
debug.data(pixel_key, "Pixel key for extraction")
|
|
||||||
|
|
||||||
encrypted = extract_from_image(stego_image, pixel_key)
|
|
||||||
|
|
||||||
# If we got data, check if it's from a different date
|
|
||||||
if encrypted:
|
|
||||||
header = parse_header(encrypted)
|
|
||||||
if header and header['date'] != date_str:
|
|
||||||
debug.print(f"Found different date in header: {header['date']} (expected {date_str})")
|
|
||||||
# Re-extract with correct date
|
|
||||||
pixel_key = derive_pixel_key(
|
|
||||||
reference_photo, day_phrase, header['date'], pin, rsa_key_data
|
|
||||||
)
|
|
||||||
encrypted = extract_from_image(stego_image, pixel_key)
|
|
||||||
|
|
||||||
if not encrypted:
|
|
||||||
debug.print("No data extracted from image")
|
|
||||||
raise ExtractionError("Could not extract data. Check your inputs.")
|
|
||||||
|
|
||||||
debug.print(f"Extracted {len(encrypted)} bytes from image")
|
|
||||||
debug.data(encrypted[:64], "First 64 bytes of extracted data")
|
|
||||||
|
|
||||||
# Decrypt and return full result
|
|
||||||
return decrypt_message(encrypted, reference_photo, day_phrase, pin, rsa_key_data)
|
|
||||||
|
|
||||||
|
|
||||||
def decode_text(
|
|
||||||
stego_image: bytes,
|
|
||||||
reference_photo: bytes,
|
|
||||||
day_phrase: str,
|
|
||||||
pin: str = "",
|
|
||||||
rsa_key_data: Optional[bytes] = None,
|
|
||||||
rsa_password: Optional[str] = None,
|
|
||||||
date_str: Optional[str] = None,
|
|
||||||
) -> str:
|
|
||||||
"""
|
|
||||||
Decode a text message from a stego image.
|
|
||||||
|
|
||||||
Convenience function that returns just the text string.
|
|
||||||
Raises an error if the content is a binary file.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
stego_image: Image containing hidden message
|
|
||||||
reference_photo: Shared reference photo bytes
|
|
||||||
day_phrase: Passphrase for the day message was encoded
|
|
||||||
pin: Static PIN (if used during encoding)
|
|
||||||
rsa_key_data: RSA private key PEM bytes (if used during encoding)
|
|
||||||
rsa_password: Password for RSA key if encrypted
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Decrypted message string
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
DecryptionError: If content is a binary file, not text
|
|
||||||
"""
|
|
||||||
debug.print("decode_text called")
|
|
||||||
result = decode(stego_image, reference_photo, day_phrase, pin, rsa_key_data, rsa_password)
|
|
||||||
|
|
||||||
if result.is_file:
|
|
||||||
# Try to decode file as text
|
|
||||||
if result.file_data:
|
|
||||||
try:
|
|
||||||
return result.file_data.decode('utf-8')
|
|
||||||
except UnicodeDecodeError:
|
|
||||||
debug.print(f"File is binary: {result.filename or 'unnamed'}")
|
|
||||||
raise DecryptionError(
|
|
||||||
f"Content is a binary file ({result.filename or 'unnamed'}), not text. "
|
|
||||||
"Use decode() instead and check result.is_file."
|
|
||||||
)
|
|
||||||
return ""
|
|
||||||
|
|
||||||
debug.print(f"Decoded text: {result.message[:100] if result.message else 'empty'}...")
|
|
||||||
return result.message or ""
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
# Version
|
|
||||||
'__version__',
|
|
||||||
|
|
||||||
# High-level API
|
|
||||||
'encode',
|
|
||||||
'encode_file',
|
|
||||||
'encode_bytes',
|
|
||||||
'decode',
|
|
||||||
'decode_text',
|
|
||||||
'generate_credentials',
|
|
||||||
|
|
||||||
# Constants
|
|
||||||
'DAY_NAMES',
|
|
||||||
'LOSSLESS_FORMATS',
|
|
||||||
'MAX_MESSAGE_SIZE',
|
|
||||||
'MAX_FILE_PAYLOAD_SIZE',
|
|
||||||
|
|
||||||
# Models
|
|
||||||
'Credentials',
|
|
||||||
'EncodeInput',
|
|
||||||
'EncodeResult',
|
|
||||||
'DecodeInput',
|
|
||||||
'DecodeResult',
|
|
||||||
'EmbedStats',
|
|
||||||
'KeyInfo',
|
|
||||||
'ValidationResult',
|
|
||||||
'FilePayload',
|
|
||||||
|
|
||||||
# Exceptions
|
|
||||||
'StegasooError',
|
|
||||||
'ValidationError',
|
|
||||||
'PinValidationError',
|
|
||||||
'MessageValidationError',
|
|
||||||
'ImageValidationError',
|
|
||||||
'KeyValidationError',
|
|
||||||
'SecurityFactorError',
|
|
||||||
'CryptoError',
|
|
||||||
'EncryptionError',
|
|
||||||
'DecryptionError',
|
|
||||||
'KeyDerivationError',
|
|
||||||
'KeyGenerationError',
|
|
||||||
'KeyPasswordError',
|
|
||||||
'SteganographyError',
|
|
||||||
'CapacityError',
|
|
||||||
'ExtractionError',
|
|
||||||
'EmbeddingError',
|
|
||||||
'InvalidHeaderError',
|
|
||||||
|
|
||||||
# Key generation
|
|
||||||
'generate_pin',
|
|
||||||
'generate_phrase',
|
|
||||||
'generate_day_phrases',
|
|
||||||
'generate_rsa_key',
|
|
||||||
'export_rsa_key_pem',
|
|
||||||
'load_rsa_key',
|
|
||||||
'get_key_info',
|
|
||||||
|
|
||||||
# Validation
|
|
||||||
'validate_pin',
|
|
||||||
'validate_message',
|
|
||||||
'validate_payload',
|
|
||||||
'validate_file_payload',
|
|
||||||
'validate_image',
|
|
||||||
'validate_rsa_key',
|
|
||||||
'validate_security_factors',
|
|
||||||
'validate_phrase',
|
|
||||||
'validate_date_string',
|
|
||||||
'require_valid_pin',
|
|
||||||
'require_valid_message',
|
|
||||||
'require_valid_payload',
|
|
||||||
'require_valid_image',
|
|
||||||
'require_valid_rsa_key',
|
|
||||||
'require_security_factors',
|
|
||||||
|
|
||||||
# Crypto
|
|
||||||
'encrypt_message',
|
|
||||||
'decrypt_message',
|
|
||||||
'decrypt_message_text',
|
|
||||||
'derive_hybrid_key',
|
|
||||||
'derive_pixel_key',
|
|
||||||
'hash_photo',
|
|
||||||
'parse_header',
|
|
||||||
'get_date_from_encrypted',
|
|
||||||
'has_argon2',
|
|
||||||
|
|
||||||
# Steganography
|
|
||||||
'embed_in_image',
|
|
||||||
'extract_from_image',
|
|
||||||
'calculate_capacity',
|
|
||||||
'get_image_dimensions',
|
|
||||||
'get_image_format',
|
|
||||||
'is_lossless_format',
|
|
||||||
|
|
||||||
# Utilities
|
|
||||||
'generate_filename',
|
|
||||||
'parse_date_from_filename',
|
|
||||||
'get_day_from_date',
|
|
||||||
'get_today_date',
|
|
||||||
'get_today_day',
|
|
||||||
'secure_delete',
|
|
||||||
'SecureDeleter',
|
|
||||||
'format_file_size',
|
|
||||||
|
|
||||||
# Debugging
|
|
||||||
'debug',
|
|
||||||
]
|
|
||||||
Reference in New Issue
Block a user