From cdff539e3eb4716db7a497c483857ae333cf39f1 Mon Sep 17 00:00:00 2001 From: "Aaron D. Lee" Date: Sat, 27 Dec 2025 23:34:05 -0500 Subject: [PATCH] Looks like it kinda works. --- v1_old_files/.dockerignore | 23 - v1_old_files/Dockerfile | 44 - v1_old_files/README.md | 137 -- v1_old_files/STEGASOO_WEB_README.md | 149 -- v1_old_files/app.py | 900 --------- v1_old_files/bip39-words.txt | 2048 --------------------- v1_old_files/requirements-ml.txt | 8 - v1_old_files/requirements.txt | 16 - v1_old_files/secureDeleter.py | 65 - v1_old_files/static/.gitkeep | 0 v1_old_files/static/favicon.svg | 24 - v1_old_files/static/logo.svg | 99 - v1_old_files/static/style.css | 257 --- v1_old_files/templates/about.html | 178 -- v1_old_files/templates/base.html | 74 - v1_old_files/templates/decode.html | 259 --- v1_old_files/templates/encode.html | 259 --- v1_old_files/templates/encode_result.html | 142 -- v1_old_files/templates/generate.html | 345 ---- v1_old_files/templates/index.html | 108 -- v1_old_files/test/story_generator.py | 244 --- v1_old_files/uploads/.gitkeep | 0 22 files changed, 5379 deletions(-) delete mode 100644 v1_old_files/.dockerignore delete mode 100644 v1_old_files/Dockerfile delete mode 100644 v1_old_files/README.md delete mode 100644 v1_old_files/STEGASOO_WEB_README.md delete mode 100644 v1_old_files/app.py delete mode 100644 v1_old_files/bip39-words.txt delete mode 100644 v1_old_files/requirements-ml.txt delete mode 100644 v1_old_files/requirements.txt delete mode 100644 v1_old_files/secureDeleter.py delete mode 100644 v1_old_files/static/.gitkeep delete mode 100644 v1_old_files/static/favicon.svg delete mode 100644 v1_old_files/static/logo.svg delete mode 100644 v1_old_files/static/style.css delete mode 100644 v1_old_files/templates/about.html delete mode 100644 v1_old_files/templates/base.html delete mode 100644 v1_old_files/templates/decode.html delete mode 100644 v1_old_files/templates/encode.html delete mode 100644 v1_old_files/templates/encode_result.html delete mode 100644 v1_old_files/templates/generate.html delete mode 100644 v1_old_files/templates/index.html delete mode 100644 v1_old_files/test/story_generator.py delete mode 100644 v1_old_files/uploads/.gitkeep diff --git a/v1_old_files/.dockerignore b/v1_old_files/.dockerignore deleted file mode 100644 index 80aac8e..0000000 --- a/v1_old_files/.dockerignore +++ /dev/null @@ -1,23 +0,0 @@ -# Git -.git -.gitignore - -# Python -__pycache__/ -*.py[cod] -venv/ - -# IDE -.vscode/ -.idea/ - -# Uploads -uploads/* - -# Docs -README.md - -# Docker -Dockerfile -docker-compose.yml -.dockerignore diff --git a/v1_old_files/Dockerfile b/v1_old_files/Dockerfile deleted file mode 100644 index 9b67308..0000000 --- a/v1_old_files/Dockerfile +++ /dev/null @@ -1,44 +0,0 @@ -FROM python:3.11-slim - -# Set environment variables -ENV PYTHONDONTWRITEBYTECODE=1 -ENV PYTHONUNBUFFERED=1 - -# Set work directory -WORKDIR /app - -# Install system dependencies -RUN apt-get update && apt-get install -y --no-install-recommends \ - gcc \ - libc-dev \ - libffi-dev \ - && rm -rf /var/lib/apt/lists/* - -# Install Python dependencies - -COPY requirements.txt . -#COPY requirements-ml.txt . - -RUN pip install --upgrade pip -RUN pip install --no-cache-dir -r requirements.txt --root-user-action=ignore -#RUN pip install --no-cache-dir -r requirements-ml.txt - -# Copy application -COPY . . - -# Create upload directory -RUN mkdir -p /tmp/stego_uploads - -# Create non-root user for security -RUN useradd -m -u 1000 stego && chown -R stego:stego /app /tmp/stego_uploads -USER stego - -# 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 in production -CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "2", "--threads", "4", "--timeout", "60", "app:app"] diff --git a/v1_old_files/README.md b/v1_old_files/README.md deleted file mode 100644 index c2113ad..0000000 --- a/v1_old_files/README.md +++ /dev/null @@ -1,137 +0,0 @@ -# Stegasoo Web Service - -A containerized Flask + Bootstrap web UI for hybrid Photo + Day-Phrase + PIN steganography. - -![Python](https://img.shields.io/badge/Python-3.11+-blue) -![Flask](https://img.shields.io/badge/Flask-3.0+-green) -![Docker](https://img.shields.io/badge/Docker-Ready-blue) -![Security](https://img.shields.io/badge/Security-AES--256--GCM-red) - -## Features - -- 🔐 **AES-256-GCM** authenticated encryption -- 🧠 **Argon2id** memory-hard key derivation (256MB) -- 🎲 **Pseudo-random pixel selection** defeats steganalysis -- 📅 **Daily key rotation** with customizable phrases (3-12 words) -- 🔢 **Static PIN** for additional entropy (6-8 digits) -- 🖼️ **Reference photo** as "something you have" -- 🌐 **Web UI** with Bootstrap 5 dark theme -- 📖 **Memory aid stories** to help memorize phrases (template or AI-powered) - -## Quick Start - -### Docker (Recommended) - -```bash -# Build and run -docker-compose up -d - -# Access at http://localhost:5000 -``` - -### Manual Installation - -```bash -# Create virtual environment -python -m venv venv -source venv/bin/activate # Linux/Mac -# or: venv\Scripts\activate # Windows - -# Install dependencies -pip install -r requirements.txt - -# Optional: Enable AI-powered story generation -pip install -r requirements-ml.txt - -# Run development server -python app.py - -# Or production with gunicorn -gunicorn --bind 0.0.0.0:5000 app:app -``` - -## Usage - -### 1. Generate Credentials - -Visit `/generate` to create: -- **7 phrases** (one per day of week, 3-12 words each) -- **1 PIN** (6-8 digits, same every day) -- **Memory aid stories** (optional, helps memorize phrases) - -Memorize these! Don't save them. - -### 2. Encode a Message - -Visit `/encode` and provide: -- **Reference photo** - A photo both parties have (NOT transmitted) -- **Carrier image** - The image to hide your message in -- **Message** - Your secret text -- **Day phrase** - Today's phrase -- **PIN** - Your static PIN - -Download the stego image and share it through any channel. - -### 3. Decode a Message - -Visit `/decode` and provide: -- **Reference photo** - Same photo used for encoding -- **Stego image** - The image containing the hidden message -- **Day phrase** - The phrase for the day it was encoded -- **PIN** - Your static PIN - -## Security Model - -| Component | Entropy | Purpose | -|-----------|---------|---------| -| Reference Photo | ~80-256 bits | Something you have | -| Day Phrase | ~33-132 bits | Something you know (rotates daily) | -| PIN | ~20-27 bits | Something you know (static) | -| **Combined** | **133-415+ bits** | **Beyond brute force** | - -### Attack Resistance - -| Attack | Result | -|--------|--------| -| Brute force | 2^133+ combinations = impossible | -| Rainbow tables | Random salt per message | -| Steganalysis | Random pixel selection defeats detection | -| GPU cracking | Argon2 requires 256MB RAM per attempt | - -## Memory Aid Stories - -The generate page can create stories to help you memorize your phrases: - -**Template-based** (default): -> Monday morning began when I discovered a **APPLE** near the **FOREST**. I had to **THUNDER** quickly, then grab the **CRYSTAL** before reaching the **BRAVE**. - -**AI-powered** (with `requirements-ml.txt`): -- Uses DistilGPT-2 (~350MB model) -- Generates more coherent, natural stories -- Words highlighted in RED CAPS - -## Configuration - -Environment variables: - -| Variable | Default | Description | -|----------|---------|-------------| -| `FLASK_ENV` | production | Flask environment | -| `SECRET_KEY` | random | Session secret (auto-generated) | - -## Production Deployment - -For production, consider: - -1. **HTTPS** - Use nginx reverse proxy with SSL -2. **Rate limiting** - Prevent abuse -3. **Logging** - Monitor for security events -4. **Memory** - Allocate at least 512MB (Argon2 needs 256MB) - -## License - -MIT License - Use responsibly. - -## ⚠️ Disclaimer - -This tool is for educational and legitimate privacy purposes only. Users are responsible for complying with applicable laws in their jurisdiction. diff --git a/v1_old_files/STEGASOO_WEB_README.md b/v1_old_files/STEGASOO_WEB_README.md deleted file mode 100644 index 8cbc439..0000000 --- a/v1_old_files/STEGASOO_WEB_README.md +++ /dev/null @@ -1,149 +0,0 @@ -# Stegasoo Web Service - -A containerized Flask + Bootstrap web UI for hybrid Photo + Day-Phrase + PIN steganography. - -![Python](https://img.shields.io/badge/Python-3.11+-blue) -![Flask](https://img.shields.io/badge/Flask-3.0+-green) -![Docker](https://img.shields.io/badge/Docker-Ready-blue) -![Security](https://img.shields.io/badge/Security-AES--256--GCM-red) - -## Features - -- 🔐 **AES-256-GCM** authenticated encryption -- 🧠 **Argon2id** memory-hard key derivation (256MB) -- 🎲 **Pseudo-random pixel selection** defeats steganalysis -- 📅 **Daily key rotation** with 3-word phrases -- 🔢 **Static PIN** for additional entropy -- 🖼️ **Reference photo** as "something you have" -- 🌐 **Web UI** with Bootstrap 5 dark theme - -## Quick Start - -### Docker (Recommended) - -```bash -# Build and run -docker-compose up -d - -# Access at http://localhost:5000 -``` - -### Manual Installation - -```bash -# Create virtual environment -python -m venv venv -source venv/bin/activate # Linux/Mac -# or: venv\Scripts\activate # Windows - -# Install dependencies -pip install -r requirements.txt - -# Run development server -python app.py - -# Or production with gunicorn -gunicorn --bind 0.0.0.0:5000 app:app -``` - -## Usage - -### 1. Generate Credentials - -Visit `/generate` to create: -- **7 three-word phrases** (one per day of week) -- **1 six-digit PIN** (same every day) - -Memorize these! Don't save them. - -### 2. Encode a Message - -Visit `/encode` and provide: -- **Reference photo** - A photo both parties have (NOT transmitted) -- **Carrier image** - The image to hide your message in -- **Message** - Your secret text -- **Day phrase** - Today's 3-word phrase -- **PIN** - Your static 6-digit PIN - -Download the stego image and share it through any channel. - -### 3. Decode a Message - -Visit `/decode` and provide: -- **Reference photo** - Same photo used for encoding -- **Stego image** - The image containing the hidden message -- **Day phrase** - The phrase for the day it was encoded -- **PIN** - Your static PIN - -## Security Model - -| Component | Entropy | Purpose | -|-----------|---------|---------| -| Reference Photo | ~80-256 bits | Something you have | -| 3-Word Phrase | ~33 bits | Something you know (rotates daily) | -| 6-Digit PIN | ~20 bits | Something you know (static) | -| **Combined** | **133+ bits** | **Beyond brute force** | - -### Attack Resistance - -| Attack | Result | -|--------|--------| -| Brute force | 2^133 combinations = impossible | -| Rainbow tables | Random salt per message | -| Steganalysis | Random pixel selection defeats detection | -| GPU cracking | Argon2 requires 256MB RAM per attempt | - -## API Endpoints - -| Endpoint | Method | Description | -|----------|--------|-------------| -| `/` | GET | Home page | -| `/generate` | GET/POST | Generate phrase card + PIN | -| `/encode` | GET/POST | Encode message in image | -| `/decode` | GET/POST | Decode message from image | -| `/about` | GET | Security information | - -## Configuration - -Environment variables: - -| Variable | Default | Description | -|----------|---------|-------------| -| `FLASK_ENV` | production | Flask environment | -| `SECRET_KEY` | random | Session secret (auto-generated) | - -## Production Deployment - -For production, consider: - -1. **HTTPS** - Use nginx reverse proxy with SSL -2. **Rate limiting** - Prevent abuse -3. **Logging** - Monitor for security events -4. **Memory** - Allocate at least 512MB (Argon2 needs 256MB) - -Example nginx config: - -```nginx -server { - listen 443 ssl; - server_name stegocrypt.example.com; - - ssl_certificate /path/to/cert.pem; - ssl_certificate_key /path/to/key.pem; - - location / { - proxy_pass http://stegocrypt:5000; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - client_max_body_size 50M; - } -} -``` - -## License - -MIT License - Use responsibly. - -## ⚠️ Disclaimer - -This tool is for educational and legitimate privacy purposes only. Users are responsible for complying with applicable laws in their jurisdiction. diff --git a/v1_old_files/app.py b/v1_old_files/app.py deleted file mode 100644 index ebb5350..0000000 --- a/v1_old_files/app.py +++ /dev/null @@ -1,900 +0,0 @@ -#!/usr/bin/env python3 -""" -Stegasoo: Steganography portal for security-minded messaging. - - Aaron D. Lee (w/ vibes) - 2025-12-27 - -Built as a learning experience with a few LLMs to see if I can make something decent. -""" - -import os -import io -import re -import secrets -import hashlib -import struct -import time -from datetime import datetime -from flask import Flask, render_template, request, send_file, jsonify, flash, redirect, url_for -from werkzeug.utils import secure_filename -from PIL import Image -from secureDeleter import SecureDeleter - -from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes -from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.primitives.asymmetric import rsa -from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives.serialization import load_pem_private_key - -try: - from argon2.low_level import hash_secret_raw, Type - HAS_ARGON2 = True -except ImportError: - HAS_ARGON2 = False - from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC - from cryptography.hazmat.primitives import hashes - -HAS_ML = False # Story generator disabled - -# ============================================================================ -# FLASK APP CONFIGURATION -# ============================================================================ - -app = Flask(__name__) -app.secret_key = secrets.token_hex(32) -app.config['MAX_CONTENT_LENGTH'] = 5 * 1024 * 1024 # 5MB max upload per file -app.config['UPLOAD_FOLDER'] = '/tmp/stego_uploads' - -# Limits -MAX_IMAGE_PIXELS = 4000000 # 4 megapixels max (e.g., 2000x2000) -MAX_MESSAGE_SIZE = 50000 # 50KB message max -MIN_PIN_LENGTH = 6 -MAX_PIN_LENGTH = 9 -MIN_RSA_BITS = 2048 -VALID_RSA_SIZES = [2048, 3072, 4096] - -os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) - -ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'bmp', 'gif'} - -# Temporary file storage for sharing (file_id -> {data, timestamp, filename}) -TEMP_FILES = {} -TEMP_FILE_EXPIRY = 300 # 5 minutes - -# ============================================================================ -# CRYPTO CONFIGURATION -# ============================================================================ - -MAGIC_HEADER = b'\x89ST3' -VERSION = 3 -SALT_SIZE = 32 -IV_SIZE = 12 -TAG_SIZE = 16 -ARGON2_TIME_COST = 4 -ARGON2_MEMORY_COST = 256 * 1024 -ARGON2_PARALLELISM = 4 - -DAY_NAMES = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'] - -# BIP-39 wordlist (loaded from file) -BIP39_FILE = os.path.join(os.path.dirname(__file__), 'bip39-words.txt') -with open(BIP39_FILE, 'r') as f: - BIP39_WORDS = [line.strip() for line in f if line.strip()] - -# ============================================================================ -# SECURE CLEANUP -# ============================================================================ - -def secure_cleanup_uploads(): - """Securely delete any stray files in uploads directory.""" - upload_dir = app.config['UPLOAD_FOLDER'] - if os.path.exists(upload_dir): - for filename in os.listdir(upload_dir): - filepath = os.path.join(upload_dir, filename) - if os.path.isfile(filepath) and not filename.startswith('.'): - try: - deleter = SecureDeleter(filepath) - deleter.execute() - except Exception as e: - os.remove(filepath) - - -def cleanup_temp_files(): - """Remove expired temporary files.""" - now = time.time() - expired = [fid for fid, info in TEMP_FILES.items() if now - info['timestamp'] > TEMP_FILE_EXPIRY] - for fid in expired: - TEMP_FILES.pop(fid, None) - -# ============================================================================ -# VALIDATION FUNCTIONS -# ============================================================================ - -def allowed_file(filename): - return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS - - -def validate_pin(pin): - """Validate PIN format: 6-9 digits, no leading zeros.""" - if not pin: - return True, "" # Empty PIN is valid (if RSA key provided) - if not pin.isdigit(): - return False, "PIN must contain only digits" - if len(pin) < MIN_PIN_LENGTH or len(pin) > MAX_PIN_LENGTH: - return False, f"PIN must be {MIN_PIN_LENGTH}-{MAX_PIN_LENGTH} digits" - if pin[0] == '0': - return False, "PIN cannot start with zero" - return True, "" - - -def validate_message(message): - """Validate message size.""" - if not message: - return False, "Message is required" - if len(message) > MAX_MESSAGE_SIZE: - return False, f"Message too long. Max {MAX_MESSAGE_SIZE // 1000}KB allowed" - return True, "" - - -def validate_image(image_data, name="Image"): - """Validate image data and dimensions.""" - try: - img = Image.open(io.BytesIO(image_data)) - width, height = img.size - num_pixels = width * height - - if num_pixels > MAX_IMAGE_PIXELS: - max_dim = int(MAX_IMAGE_PIXELS ** 0.5) - return False, f"{name} too large ({width}x{height} = {num_pixels:,} pixels). Max ~{MAX_IMAGE_PIXELS:,} pixels ({max_dim}x{max_dim})" - return True, "" - except Exception as e: - return False, f"Could not read {name}: {str(e)}" - - -def validate_rsa_key(key_data, password=None): - """ - Validate RSA private key. - Returns (is_valid, error_message, key_size_bits) - """ - if not key_data: - return True, "", 0 # Empty key is valid (if PIN provided) - - try: - # Try to load the key - if password: - private_key = load_pem_private_key(key_data, password=password.encode(), backend=default_backend()) - else: - # Try without password first - try: - private_key = load_pem_private_key(key_data, password=None, backend=default_backend()) - except TypeError: - # Key is encrypted but no password provided - return False, "RSA key is password-protected. Please enter the password.", 0 - - # Check key size - key_size = private_key.key_size - if key_size < MIN_RSA_BITS: - return False, f"RSA key must be at least {MIN_RSA_BITS} bits (got {key_size})", 0 - - return True, "", key_size - - except ValueError as e: - if "password" in str(e).lower() or "encrypted" in str(e).lower(): - return False, "Incorrect password for RSA key", 0 - return False, f"Invalid RSA key format: {str(e)}", 0 - except Exception as e: - return False, f"Could not load RSA key: {str(e)}", 0 - - -def validate_security_factors(pin, rsa_key_data): - """Ensure at least one security factor is provided.""" - has_pin = bool(pin and pin.strip()) - has_key = bool(rsa_key_data and len(rsa_key_data) > 0) - - if not has_pin and not has_key: - return False, "You must provide at least a PIN or RSA Key" - return True, "" - - -# ============================================================================ -# RSA KEY GENERATION -# ============================================================================ - -def generate_rsa_key(bits=2048): - """Generate RSA private key.""" - if bits not in VALID_RSA_SIZES: - bits = 2048 - - private_key = rsa.generate_private_key( - public_exponent=65537, - key_size=bits, - backend=default_backend() - ) - return private_key - - -def export_rsa_key_pem(private_key, password=None): - """Export RSA key to PEM format, optionally encrypted.""" - if password: - encryption = serialization.BestAvailableEncryption(password.encode()) - else: - encryption = serialization.NoEncryption() - - pem = private_key.private_bytes( - encoding=serialization.Encoding.PEM, - format=serialization.PrivateFormat.PKCS8, - encryption_algorithm=encryption - ) - return pem - - -# ============================================================================ -# HELPER FUNCTIONS -# ============================================================================ - -def generate_pin(length=6): - """Generate a random PIN of specified length (6-9 digits).""" - length = max(MIN_PIN_LENGTH, min(MAX_PIN_LENGTH, length)) - first_digit = str(secrets.randbelow(9) + 1) # 1-9 - rest = ''.join(str(secrets.randbelow(10)) for _ in range(length - 1)) - return first_digit + rest - - -def generate_day_phrases(words_per_phrase=3): - phrases = {} - for day in DAY_NAMES: - words = [secrets.choice(BIP39_WORDS) for _ in range(words_per_phrase)] - phrases[day] = ' '.join(words) - return phrases - - -def hash_photo(image_data): - """Compute deterministic hash of photo content.""" - img = Image.open(io.BytesIO(image_data)) - img = img.convert('RGB') - pixels = img.tobytes() - h = hashlib.sha256(pixels).digest() - h = hashlib.sha256(h + pixels[:1024]).digest() - return h - - -def derive_hybrid_key(photo_data, day_phrase, date_str, salt, pin="", rsa_key_data=None): - """Derive encryption key from photo + phrase + PIN + RSA key + date + salt.""" - photo_hash = hash_photo(photo_data) - - key_material = ( - photo_hash + - day_phrase.lower().encode() + - pin.encode() + - date_str.encode() + - salt - ) - - # Add RSA key hash if provided - if rsa_key_data: - key_material += hashlib.sha256(rsa_key_data).digest() - - if HAS_ARGON2: - key = hash_secret_raw( - secret=key_material, - salt=salt[:32], - time_cost=ARGON2_TIME_COST, - memory_cost=ARGON2_MEMORY_COST, - parallelism=ARGON2_PARALLELISM, - hash_len=32, - type=Type.ID - ) - else: - kdf = PBKDF2HMAC( - algorithm=hashes.SHA512(), - length=32, - salt=salt, - iterations=600000, - backend=default_backend() - ) - key = kdf.derive(key_material) - - return key - - -def derive_pixel_key(photo_data, day_phrase, date_str, pin="", rsa_key_data=None): - """Derive key for pixel selection.""" - photo_hash = hash_photo(photo_data) - material = photo_hash + day_phrase.lower().encode() + pin.encode() + date_str.encode() - - if rsa_key_data: - material += hashlib.sha256(rsa_key_data).digest() - - return hashlib.sha256(material + b"pixel_selection").digest() - - -def encrypt_message(message, photo_data, day_phrase, date_str, pin="", rsa_key_data=None): - """Encrypt message using hybrid key derivation.""" - salt = secrets.token_bytes(SALT_SIZE) - key = derive_hybrid_key(photo_data, day_phrase, date_str, salt, pin, rsa_key_data) - iv = secrets.token_bytes(IV_SIZE) - - if isinstance(message, str): - message = message.encode() - - padding_len = secrets.randbelow(256) + 64 - padded_len = ((len(message) + padding_len + 255) // 256) * 256 - padding_needed = padded_len - len(message) - padding = secrets.token_bytes(padding_needed - 4) + struct.pack('>I', len(message)) - padded_message = message + padding - - cipher = Cipher(algorithms.AES(key), modes.GCM(iv), backend=default_backend()) - encryptor = cipher.encryptor() - encryptor.authenticate_additional_data(MAGIC_HEADER + bytes([VERSION])) - ciphertext = encryptor.update(padded_message) + encryptor.finalize() - - date_bytes = date_str.encode() - - return ( - MAGIC_HEADER + - bytes([VERSION]) + - bytes([len(date_bytes)]) + - date_bytes + - salt + - iv + - encryptor.tag + - ciphertext - ) - - -def generate_pixel_indices(key, num_pixels, num_needed): - """Generate pseudo-random pixel indices.""" - if num_needed >= num_pixels // 2: - nonce = b'\x00' * 16 - cipher = Cipher(algorithms.ChaCha20(key, nonce), mode=None, backend=default_backend()) - encryptor = cipher.encryptor() - - indices = list(range(num_pixels)) - random_bytes = encryptor.update(b'\x00' * (num_pixels * 4)) - - for i in range(num_pixels - 1, 0, -1): - j_bytes = random_bytes[(num_pixels - 1 - i) * 4:(num_pixels - i) * 4] - j = int.from_bytes(j_bytes, 'big') % (i + 1) - indices[i], indices[j] = indices[j], indices[i] - - return indices[:num_needed] - - selected = [] - used = set() - - nonce = b'\x00' * 16 - cipher = Cipher(algorithms.ChaCha20(key, nonce), mode=None, backend=default_backend()) - encryptor = cipher.encryptor() - - bytes_needed = (num_needed * 2) * 4 - random_bytes = encryptor.update(b'\x00' * bytes_needed) - - byte_offset = 0 - while len(selected) < num_needed and byte_offset < len(random_bytes) - 4: - idx = int.from_bytes(random_bytes[byte_offset:byte_offset + 4], 'big') % num_pixels - byte_offset += 4 - - if idx not in used: - used.add(idx) - selected.append(idx) - - while len(selected) < num_needed: - extra_bytes = encryptor.update(b'\x00' * 4) - idx = int.from_bytes(extra_bytes, 'big') % num_pixels - if idx not in used: - used.add(idx) - selected.append(idx) - - return selected - - -def embed_in_image(carrier_data, encrypted_data, pixel_key, bits_per_channel=1): - """Embed encrypted data in carrier image. Returns PNG bytes.""" - img = Image.open(io.BytesIO(carrier_data)) - if img.mode != 'RGB': - img = img.convert('RGB') - - pixels = list(img.getdata()) - num_pixels = len(pixels) - - bits_per_pixel = 3 * bits_per_channel - max_bytes = (num_pixels * bits_per_pixel) // 8 - - data_with_len = struct.pack('>I', len(encrypted_data)) + encrypted_data - - if len(data_with_len) > max_bytes: - raise ValueError(f"Carrier too small. Need {len(data_with_len)} bytes, have {max_bytes}") - - binary_data = ''.join(format(b, '08b') for b in data_with_len) - pixels_needed = (len(binary_data) + bits_per_pixel - 1) // bits_per_pixel - - selected_indices = generate_pixel_indices(pixel_key, num_pixels, pixels_needed) - - new_pixels = list(pixels) - clear_mask = 0xFF ^ ((1 << bits_per_channel) - 1) - - bit_idx = 0 - for pixel_idx in selected_indices: - if bit_idx >= len(binary_data): - break - - r, g, b = new_pixels[pixel_idx] - - for channel_idx, channel_val in enumerate([r, g, b]): - if bit_idx >= len(binary_data): - break - bits = binary_data[bit_idx:bit_idx + bits_per_channel].ljust(bits_per_channel, '0') - new_val = (channel_val & clear_mask) | int(bits, 2) - - if channel_idx == 0: - r = new_val - elif channel_idx == 1: - g = new_val - else: - b = new_val - - bit_idx += bits_per_channel - - new_pixels[pixel_idx] = (r, g, b) - - stego_img = Image.new('RGB', img.size) - stego_img.putdata(new_pixels) - - output = io.BytesIO() - stego_img.save(output, 'PNG') - output.seek(0) - - return output.getvalue(), { - 'pixels_modified': len(selected_indices), - 'total_pixels': num_pixels, - 'capacity_used': len(data_with_len) / max_bytes - } - - -def extract_from_image(image_data, pixel_key, bits_per_channel=1): - """Extract hidden data from image.""" - img = Image.open(io.BytesIO(image_data)) - if img.mode != 'RGB': - img = img.convert('RGB') - - pixels = list(img.getdata()) - num_pixels = len(pixels) - bits_per_pixel = 3 * bits_per_channel - - initial_pixels = (32 + bits_per_pixel - 1) // bits_per_pixel + 10 - initial_indices = generate_pixel_indices(pixel_key, num_pixels, initial_pixels) - - binary_data = '' - for pixel_idx in initial_indices: - r, g, b = pixels[pixel_idx] - for channel in [r, g, b]: - for bit_pos in range(bits_per_channel - 1, -1, -1): - binary_data += str((channel >> bit_pos) & 1) - - try: - length_bits = binary_data[:32] - data_length = struct.unpack('>I', int(length_bits, 2).to_bytes(4, 'big'))[0] - except: - return None - - max_possible = (num_pixels * bits_per_pixel) // 8 - 4 - if data_length > max_possible or data_length < 10: - return None - - total_bits = (4 + data_length) * 8 - pixels_needed = (total_bits + bits_per_pixel - 1) // bits_per_pixel - - selected_indices = generate_pixel_indices(pixel_key, num_pixels, pixels_needed) - - binary_data = '' - for pixel_idx in selected_indices: - r, g, b = pixels[pixel_idx] - for channel in [r, g, b]: - for bit_pos in range(bits_per_channel - 1, -1, -1): - binary_data += str((channel >> bit_pos) & 1) - - data_bits = binary_data[32:32 + (data_length * 8)] - - data_bytes = bytearray() - for i in range(0, len(data_bits), 8): - byte_bits = data_bits[i:i+8] - if len(byte_bits) == 8: - data_bytes.append(int(byte_bits, 2)) - - return bytes(data_bytes) - - -def parse_header(encrypted_data): - """Parse v3 header.""" - if len(encrypted_data) < 10 or encrypted_data[:4] != MAGIC_HEADER: - return None - - date_len = encrypted_data[5] - date_str = encrypted_data[6:6+date_len].decode() - - offset = 6 + date_len - salt = encrypted_data[offset:offset+SALT_SIZE] - offset += SALT_SIZE - iv = encrypted_data[offset:offset+IV_SIZE] - offset += IV_SIZE - tag = encrypted_data[offset:offset+TAG_SIZE] - offset += TAG_SIZE - ciphertext = encrypted_data[offset:] - - return {'date': date_str, 'salt': salt, 'iv': iv, 'tag': tag, 'ciphertext': ciphertext} - - -def decrypt_message(encrypted_data, photo_data, day_phrase, pin="", rsa_key_data=None): - """Decrypt message.""" - header = parse_header(encrypted_data) - if not header: - return None - - key = derive_hybrid_key(photo_data, day_phrase, header['date'], header['salt'], pin, rsa_key_data) - - cipher = Cipher( - algorithms.AES(key), - modes.GCM(header['iv'], header['tag']), - backend=default_backend() - ) - decryptor = cipher.decryptor() - decryptor.authenticate_additional_data(MAGIC_HEADER + bytes([VERSION])) - - try: - padded_plaintext = decryptor.update(header['ciphertext']) + decryptor.finalize() - original_length = struct.unpack('>I', padded_plaintext[-4:])[0] - return padded_plaintext[:original_length].decode('utf-8') - except: - return None - - -# ============================================================================ -# FLASK ROUTES -# ============================================================================ - -@app.route('/') -def index(): - return render_template('index.html') - - -@app.route('/generate', methods=['GET', 'POST']) -def generate(): - if request.method == 'POST': - words_per_phrase = int(request.form.get('words_per_phrase', 3)) - - # Security factor options - use_pin = request.form.get('use_pin') == 'on' - use_rsa = request.form.get('use_rsa') == 'on' - - # Validate at least one factor selected - if not use_pin and not use_rsa: - flash('You must select at least one security factor (PIN or RSA Key)', 'error') - return render_template('generate.html', generated=False, has_ml=HAS_ML) - - pin_length = int(request.form.get('pin_length', 6)) - rsa_bits = int(request.form.get('rsa_bits', 2048)) - - # Clamp values to valid ranges - words_per_phrase = max(3, min(12, words_per_phrase)) - pin_length = max(MIN_PIN_LENGTH, min(MAX_PIN_LENGTH, pin_length)) - if rsa_bits not in VALID_RSA_SIZES: - rsa_bits = 2048 - - phrases = generate_day_phrases(words_per_phrase) - - # Generate PIN if selected - pin = generate_pin(pin_length) if use_pin else None - - # Generate RSA key if selected - rsa_key_pem = None - if use_rsa: - private_key = generate_rsa_key(rsa_bits) - rsa_key_pem = export_rsa_key_pem(private_key, password=None).decode('utf-8') - - # Calculate entropy - phrase_entropy = words_per_phrase * 11 - pin_entropy = int(pin_length * 3.32) if use_pin else 0 - # RSA key adds significant entropy (conservatively estimate effective security) - rsa_entropy = min(rsa_bits // 16, 128) if use_rsa else 0 # ~128 bits effective for 2048-bit - total_entropy = phrase_entropy + pin_entropy + rsa_entropy - - return render_template('generate.html', - phrases=phrases, - pin=pin, - days=DAY_NAMES, - generated=True, - words_per_phrase=words_per_phrase, - pin_length=pin_length if use_pin else None, - use_pin=use_pin, - use_rsa=use_rsa, - rsa_bits=rsa_bits, - rsa_key_pem=rsa_key_pem, - phrase_entropy=phrase_entropy, - pin_entropy=pin_entropy, - rsa_entropy=rsa_entropy, - total_entropy=total_entropy, - has_ml=HAS_ML) - - return render_template('generate.html', generated=False, has_ml=HAS_ML) - - -@app.route('/generate/download-key', methods=['POST']) -def download_key(): - """Download RSA key as password-protected PEM file.""" - key_pem = request.form.get('key_pem', '') - password = request.form.get('key_password', '') - - if not key_pem: - flash('No key to download', 'error') - return redirect(url_for('generate')) - - if not password or len(password) < 8: - flash('Password must be at least 8 characters', 'error') - return redirect(url_for('generate')) - - try: - # Load the unencrypted key - private_key = load_pem_private_key(key_pem.encode(), password=None, backend=default_backend()) - - # Re-export with password protection - encrypted_pem = export_rsa_key_pem(private_key, password=password) - - # Generate filename - key_id = secrets.token_hex(4) - filename = f'stegasoo_key_{private_key.key_size}_{key_id}.pem' - - return send_file( - io.BytesIO(encrypted_pem), - mimetype='application/x-pem-file', - as_attachment=True, - download_name=filename - ) - except Exception as e: - flash(f'Error creating key file: {str(e)}', 'error') - return redirect(url_for('generate')) - - -@app.route('/encode', methods=['GET', 'POST']) -def encode(): - day_of_week = datetime.now().strftime("%A") - - if request.method == 'POST': - try: - # Get files - ref_photo = request.files.get('reference_photo') - carrier = request.files.get('carrier') - rsa_key_file = request.files.get('rsa_key') - - if not ref_photo or not carrier: - flash('Both reference photo and carrier image are required', 'error') - return render_template('encode.html', day_of_week=day_of_week) - - if not allowed_file(ref_photo.filename) or not allowed_file(carrier.filename): - flash('Invalid file type. Use PNG, JPG, or BMP', 'error') - return render_template('encode.html', day_of_week=day_of_week) - - # Get form data - message = request.form.get('message', '') - day_phrase = request.form.get('day_phrase', '') - pin = request.form.get('pin', '').strip() - rsa_password = request.form.get('rsa_password', '') - - # Validate message - valid, error = validate_message(message) - if not valid: - flash(error, 'error') - return render_template('encode.html', day_of_week=day_of_week) - - if not day_phrase: - flash('Day phrase is required', 'error') - return render_template('encode.html', day_of_week=day_of_week) - - # Read files - ref_data = ref_photo.read() - carrier_data = carrier.read() - rsa_key_data = rsa_key_file.read() if rsa_key_file and rsa_key_file.filename else None - - # Validate security factors - valid, error = validate_security_factors(pin, rsa_key_data) - if not valid: - flash(error, 'error') - return render_template('encode.html', day_of_week=day_of_week) - - # Validate PIN if provided - if pin: - valid, error = validate_pin(pin) - if not valid: - flash(error, 'error') - return render_template('encode.html', day_of_week=day_of_week) - - # Validate RSA key if provided - if rsa_key_data: - valid, error, key_size = validate_rsa_key(rsa_key_data, rsa_password if rsa_password else None) - if not valid: - flash(error, 'error') - return render_template('encode.html', day_of_week=day_of_week) - - # Validate carrier image - valid, error = validate_image(carrier_data, "Carrier image") - if not valid: - flash(error, 'error') - return render_template('encode.html', day_of_week=day_of_week) - - # Get date - use client's local date if provided - client_date = request.form.get('client_date', '').strip() - if client_date and len(client_date) == 10 and client_date[4] == '-' and client_date[7] == '-': - date_str = client_date - else: - date_str = datetime.now().strftime('%Y-%m-%d') - - # Encrypt - encrypted = encrypt_message(message, ref_data, day_phrase, date_str, pin, rsa_key_data) - - # Get pixel key - pixel_key = derive_pixel_key(ref_data, day_phrase, date_str, pin, rsa_key_data) - - # Embed - stego_data, stats = embed_in_image(carrier_data, encrypted, pixel_key) - - # Generate filename and file ID - filename = f'{secrets.token_hex(4)}_{date_str.replace("-", "")}.png' - file_id = secrets.token_urlsafe(16) - - # Store temporarily for download/share - cleanup_temp_files() - TEMP_FILES[file_id] = { - 'data': stego_data, - 'filename': filename, - 'timestamp': time.time() - } - - return redirect(url_for('encode_result', file_id=file_id)) - - except Exception as e: - flash(f'Error: {str(e)}', 'error') - return render_template('encode.html', day_of_week=day_of_week) - - return render_template('encode.html', day_of_week=day_of_week) - - -@app.route('/encode/result/') -def encode_result(file_id): - if file_id not in TEMP_FILES: - flash('File expired or not found. Please encode again.', 'error') - return redirect(url_for('encode')) - - file_info = TEMP_FILES[file_id] - return render_template('encode_result.html', - file_id=file_id, - filename=file_info['filename']) - - -@app.route('/encode/download/') -def encode_download(file_id): - if file_id not in TEMP_FILES: - flash('File expired or not found.', 'error') - return redirect(url_for('encode')) - - file_info = TEMP_FILES[file_id] - - return send_file( - io.BytesIO(file_info['data']), - mimetype='image/png', - as_attachment=True, - download_name=file_info['filename'] - ) - - -@app.route('/encode/file/') -def encode_file(file_id): - """Serve file for Web Share API (inline, not attachment).""" - if file_id not in TEMP_FILES: - return "Not found", 404 - - file_info = TEMP_FILES[file_id] - - return send_file( - io.BytesIO(file_info['data']), - mimetype='image/png', - as_attachment=False, - download_name=file_info['filename'] - ) - - -@app.route('/encode/cleanup/', methods=['POST']) -def encode_cleanup(file_id): - """Manually cleanup a file after sharing.""" - TEMP_FILES.pop(file_id, None) - return jsonify({'status': 'ok'}) - - -@app.route('/decode', methods=['GET', 'POST']) -def decode(): - if request.method == 'POST': - try: - # Get files - ref_photo = request.files.get('reference_photo') - stego_image = request.files.get('stego_image') - rsa_key_file = request.files.get('rsa_key') - - if not ref_photo or not stego_image: - flash('Both reference photo and stego image are required', 'error') - return render_template('decode.html') - - # Get form data - day_phrase = request.form.get('day_phrase', '') - pin = request.form.get('pin', '').strip() - rsa_password = request.form.get('rsa_password', '') - - if not day_phrase: - flash('Day phrase is required', 'error') - return render_template('decode.html') - - # Read files - ref_data = ref_photo.read() - stego_data = stego_image.read() - rsa_key_data = rsa_key_file.read() if rsa_key_file and rsa_key_file.filename else None - - # Validate security factors - valid, error = validate_security_factors(pin, rsa_key_data) - if not valid: - flash(error, 'error') - return render_template('decode.html') - - # Validate PIN if provided - if pin: - valid, error = validate_pin(pin) - if not valid: - flash(error, 'error') - return render_template('decode.html') - - # Validate RSA key if provided - if rsa_key_data: - valid, error, key_size = validate_rsa_key(rsa_key_data, rsa_password if rsa_password else None) - if not valid: - flash(error, 'error') - return render_template('decode.html') - - # Try to extract and decrypt - date_str = datetime.now().strftime('%Y-%m-%d') - pixel_key = derive_pixel_key(ref_data, day_phrase, date_str, pin, rsa_key_data) - - encrypted = extract_from_image(stego_data, pixel_key) - - if encrypted: - header = parse_header(encrypted) - if header and header['date'] != date_str: - pixel_key = derive_pixel_key(ref_data, day_phrase, header['date'], pin, rsa_key_data) - encrypted = extract_from_image(stego_data, pixel_key) - - if not encrypted: - flash('Could not extract data. Check your inputs.', 'error') - return render_template('decode.html') - - message = decrypt_message(encrypted, ref_data, day_phrase, pin, rsa_key_data) - - if message: - return render_template('decode.html', decoded_message=message) - else: - flash('Decryption failed. Wrong phrase, PIN, RSA key, or reference photo.', 'error') - return render_template('decode.html') - - except Exception as e: - flash(f'Error: {str(e)}', 'error') - return render_template('decode.html') - - return render_template('decode.html') - - -@app.route('/about') -def about(): - return render_template('about.html', has_argon2=HAS_ARGON2) - - -# ============================================================================ -# MAIN -# ============================================================================ - -if __name__ == '__main__': - app.run(host='0.0.0.0', port=5000, debug=False) diff --git a/v1_old_files/bip39-words.txt b/v1_old_files/bip39-words.txt deleted file mode 100644 index 942040e..0000000 --- a/v1_old_files/bip39-words.txt +++ /dev/null @@ -1,2048 +0,0 @@ -abandon -ability -able -about -above -absent -absorb -abstract -absurd -abuse -access -accident -account -accuse -achieve -acid -acoustic -acquire -across -act -action -actor -actress -actual -adapt -add -addict -address -adjust -admit -adult -advance -advice -aerobic -affair -afford -afraid -again -age -agent -agree -ahead -aim -air -airport -aisle -alarm -album -alcohol -alert -alien -all -alley -allow -almost -alone -alpha -already -also -alter -always -amateur -amazing -among -amount -amused -analyst -anchor -ancient -anger -angle -angry -animal -ankle -announce -annual -another -answer -antenna -antique -anxiety -any -apart -apology -appear -apple -approve -april -arch -arctic -area -arena -argue -arm -armed -armor -army -around -arrange -arrest -arrive -arrow -art -artefact -artist -artwork -ask -aspect -assault -asset -assist -assume -asthma -athlete -atom -attack -attend -attitude -attract -auction -audit -august -aunt -author -auto -autumn -average -avocado -avoid -awake -aware -away -awesome -awful -awkward -axis -baby -bachelor -bacon -badge -bag -balance -balcony -ball -bamboo -banana -banner -bar -barely -bargain -barrel -base -basic -basket -battle -beach -bean -beauty -because -become -beef -before -begin -behave -behind -believe -below -belt -bench -benefit -best -betray -better -between -beyond -bicycle -bid -bike -bind -biology -bird -birth -bitter -black -blade -blame -blanket -blast -bleak -bless -blind -blood -blossom -blouse -blue -blur -blush -board -boat -body -boil -bomb -bone -bonus -book -boost -border -boring -borrow -boss -bottom -bounce -box -boy -bracket -brain -brand -brass -brave -bread -breeze -brick -bridge -brief -bright -bring -brisk -broccoli -broken -bronze -broom -brother -brown -brush -bubble -buddy -budget -buffalo -build -bulb -bulk -bullet -bundle -bunker -burden -burger -burst -bus -business -busy -butter -buyer -buzz -cabbage -cabin -cable -cactus -cage -cake -call -calm -camera -camp -can -canal -cancel -candy -cannon -canoe -canvas -canyon -capable -capital -captain -car -carbon -card -cargo -carpet -carry -cart -case -cash -casino -castle -casual -cat -catalog -catch -category -cattle -caught -cause -caution -cave -ceiling -celery -cement -census -century -cereal -certain -chair -chalk -champion -change -chaos -chapter -charge -chase -chat -cheap -check -cheese -chef -cherry -chest -chicken -chief -child -chimney -choice -choose -chronic -chuckle -chunk -churn -cigar -cinnamon -circle -citizen -city -civil -claim -clap -clarify -claw -clay -clean -clerk -clever -click -client -cliff -climb -clinic -clip -clock -clog -close -cloth -cloud -clown -club -clump -cluster -clutch -coach -coast -coconut -code -coffee -coil -coin -collect -color -column -combine -come -comfort -comic -common -company -concert -conduct -confirm -congress -connect -consider -control -convince -cook -cool -copper -copy -coral -core -corn -correct -cost -cotton -couch -country -couple -course -cousin -cover -coyote -crack -cradle -craft -cram -crane -crash -crater -crawl -crazy -cream -credit -creek -crew -cricket -crime -crisp -critic -crop -cross -crouch -crowd -crucial -cruel -cruise -crumble -crunch -crush -cry -crystal -cube -culture -cup -cupboard -curious -current -curtain -curve -cushion -custom -cute -cycle -dad -damage -damp -dance -danger -daring -dash -daughter -dawn -day -deal -debate -debris -decade -december -decide -decline -decorate -decrease -deer -defense -define -defy -degree -delay -deliver -demand -demise -denial -dentist -deny -depart -depend -deposit -depth -deputy -derive -describe -desert -design -desk -despair -destroy -detail -detect -develop -device -devote -diagram -dial -diamond -diary -dice -diesel -diet -differ -digital -dignity -dilemma -dinner -dinosaur -direct -dirt -disagree -discover -disease -dish -dismiss -disorder -display -distance -divert -divide -divorce -dizzy -doctor -document -dog -doll -dolphin -domain -donate -donkey -donor -door -dose -double -dove -draft -dragon -drama -drastic -draw -dream -dress -drift -drill -drink -drip -drive -drop -drum -dry -duck -dumb -dune -during -dust -dutch -duty -dwarf -dynamic -eager -eagle -early -earn -earth -easily -east -easy -echo -ecology -economy -edge -edit -educate -effort -egg -eight -either -elbow -elder -electric -elegant -element -elephant -elevator -elite -else -embark -embody -embrace -emerge -emotion -employ -empower -empty -enable -enact -end -endless -endorse -enemy -energy -enforce -engage -engine -enhance -enjoy -enlist -enough -enrich -enroll -ensure -enter -entire -entry -envelope -episode -equal -equip -era -erase -erode -erosion -error -erupt -escape -essay -essence -estate -eternal -ethics -evidence -evil -evoke -evolve -exact -example -excess -exchange -excite -exclude -excuse -execute -exercise -exhaust -exhibit -exile -exist -exit -exotic -expand -expect -expire -explain -expose -express -extend -extra -eye -eyebrow -fabric -face -faculty -fade -faint -faith -fall -false -fame -family -famous -fan -fancy -fantasy -farm -fashion -fat -fatal -father -fatigue -fault -favorite -feature -february -federal -fee -feed -feel -female -fence -festival -fetch -fever -few -fiber -fiction -field -figure -file -film -filter -final -find -fine -finger -finish -fire -firm -first -fiscal -fish -fit -fitness -fix -flag -flame -flash -flat -flavor -flee -flight -flip -float -flock -floor -flower -fluid -flush -fly -foam -focus -fog -foil -fold -follow -food -foot -force -forest -forget -fork -fortune -forum -forward -fossil -foster -found -fox -fragile -frame -frequent -fresh -friend -fringe -frog -front -frost -frown -frozen -fruit -fuel -fun -funny -furnace -fury -future -gadget -gain -galaxy -gallery -game -gap -garage -garbage -garden -garlic -garment -gas -gasp -gate -gather -gauge -gaze -general -genius -genre -gentle -genuine -gesture -ghost -giant -gift -giggle -ginger -giraffe -girl -give -glad -glance -glare -glass -glide -glimpse -globe -gloom -glory -glove -glow -glue -goat -goddess -gold -good -goose -gorilla -gospel -gossip -govern -gown -grab -grace -grain -grant -grape -grass -gravity -great -green -grid -grief -grit -grocery -group -grow -grunt -guard -guess -guide -guilt -guitar -gun -gym -habit -hair -half -hammer -hamster -hand -happy -harbor -hard -harsh -harvest -hat -have -hawk -hazard -head -health -heart -heavy -hedgehog -height -hello -helmet -help -hen -hero -hidden -high -hill -hint -hip -hire -history -hobby -hockey -hold -hole -holiday -hollow -home -honey -hood -hope -horn -horror -horse -hospital -host -hotel -hour -hover -hub -huge -human -humble -humor -hundred -hungry -hunt -hurdle -hurry -hurt -husband -hybrid -ice -icon -idea -identify -idle -ignore -ill -illegal -illness -image -imitate -immense -immune -impact -impose -improve -impulse -inch -include -income -increase -index -indicate -indoor -industry -infant -inflict -inform -inhale -inherit -initial -inject -injury -inmate -inner -innocent -input -inquiry -insane -insect -inside -inspire -install -intact -interest -into -invest -invite -involve -iron -island -isolate -issue -item -ivory -jacket -jaguar -jar -jazz -jealous -jeans -jelly -jewel -job -join -joke -journey -joy -judge -juice -jump -jungle -junior -junk -just -kangaroo -keen -keep -ketchup -key -kick -kid -kidney -kind -kingdom -kiss -kit -kitchen -kite -kitten -kiwi -knee -knife -knock -know -lab -label -labor -ladder -lady -lake -lamp -language -laptop -large -later -latin -laugh -laundry -lava -law -lawn -lawsuit -layer -lazy -leader -leaf -learn -leave -lecture -left -leg -legal -legend -leisure -lemon -lend -length -lens -leopard -lesson -letter -level -liar -liberty -library -license -life -lift -light -like -limb -limit -link -lion -liquid -list -little -live -lizard -load -loan -lobster -local -lock -logic -lonely -long -loop -lottery -loud -lounge -love -loyal -lucky -luggage -lumber -lunar -lunch -luxury -lyrics -machine -mad -magic -magnet -maid -mail -main -major -make -mammal -man -manage -mandate -mango -mansion -manual -maple -marble -march -margin -marine -market -marriage -mask -mass -master -match -material -math -matrix -matter -maximum -maze -meadow -mean -measure -meat -mechanic -medal -media -melody -melt -member -memory -mention -menu -mercy -merge -merit -merry -mesh -message -metal -method -middle -midnight -milk -million -mimic -mind -minimum -minor -minute -miracle -mirror -misery -miss -mistake -mix -mixed -mixture -mobile -model -modify -mom -moment -monitor -monkey -monster -month -moon -moral -more -morning -mosquito -mother -motion -motor -mountain -mouse -move -movie -much -muffin -mule -multiply -muscle -museum -mushroom -music -must -mutual -myself -mystery -myth -naive -name -napkin -narrow -nasty -nation -nature -near -neck -need -negative -neglect -neither -nephew -nerve -nest -net -network -neutral -never -news -next -nice -night -noble -noise -nominee -noodle -normal -north -nose -notable -note -nothing -notice -novel -now -nuclear -number -nurse -nut -oak -obey -object -oblige -obscure -observe -obtain -obvious -occur -ocean -october -odor -off -offer -office -often -oil -okay -old -olive -olympic -omit -once -one -onion -online -only -open -opera -opinion -oppose -option -orange -orbit -orchard -order -ordinary -organ -orient -original -orphan -ostrich -other -outdoor -outer -output -outside -oval -oven -over -own -owner -oxygen -oyster -ozone -pact -paddle -page -pair -palace -palm -panda -panel -panic -panther -paper -parade -parent -park -parrot -party -pass -patch -path -patient -patrol -pattern -pause -pave -payment -peace -peanut -pear -peasant -pelican -pen -penalty -pencil -people -pepper -perfect -permit -person -pet -phone -photo -phrase -physical -piano -picnic -picture -piece -pig -pigeon -pill -pilot -pink -pioneer -pipe -pistol -pitch -pizza -place -planet -plastic -plate -play -please -pledge -pluck -plug -plunge -poem -poet -point -polar -pole -police -pond -pony -pool -popular -portion -position -possible -post -potato -pottery -poverty -powder -power -practice -praise -predict -prefer -prepare -present -pretty -prevent -price -pride -primary -print -priority -prison -private -prize -problem -process -produce -profit -program -project -promote -proof -property -prosper -protect -proud -provide -public -pudding -pull -pulp -pulse -pumpkin -punch -pupil -puppy -purchase -purity -purpose -purse -push -put -puzzle -pyramid -quality -quantum -quarter -question -quick -quit -quiz -quote -rabbit -raccoon -race -rack -radar -radio -rail -rain -raise -rally -ramp -ranch -random -range -rapid -rare -rate -rather -raven -raw -razor -ready -real -reason -rebel -rebuild -recall -receive -recipe -record -recycle -reduce -reflect -reform -refuse -region -regret -regular -reject -relax -release -relief -rely -remain -remember -remind -remove -render -renew -rent -reopen -repair -repeat -replace -report -require -rescue -resemble -resist -resource -response -result -retire -retreat -return -reunion -reveal -review -reward -rhythm -rib -ribbon -rice -rich -ride -ridge -rifle -right -rigid -ring -riot -ripple -risk -ritual -rival -river -road -roast -robot -robust -rocket -romance -roof -rookie -room -rose -rotate -rough -round -route -royal -rubber -rude -rug -rule -run -runway -rural -sad -saddle -sadness -safe -sail -salad -salmon -salon -salt -salute -same -sample -sand -satisfy -satoshi -sauce -sausage -save -say -scale -scan -scare -scatter -scene -scheme -school -science -scissors -scorpion -scout -scrap -screen -script -scrub -sea -search -season -seat -second -secret -section -security -seed -seek -segment -select -sell -seminar -senior -sense -sentence -series -service -session -settle -setup -seven -shadow -shaft -shallow -share -shed -shell -sheriff -shield -shift -shine -ship -shiver -shock -shoe -shoot -shop -short -shoulder -shove -shrimp -shrug -shuffle -shy -sibling -sick -side -siege -sight -sign -silent -silk -silly -silver -similar -simple -since -sing -siren -sister -situate -six -size -skate -sketch -ski -skill -skin -skirt -skull -slab -slam -sleep -slender -slice -slide -slight -slim -slogan -slot -slow -slush -small -smart -smile -smoke -smooth -snack -snake -snap -sniff -snow -soap -soccer -social -sock -soda -soft -solar -soldier -solid -solution -solve -someone -song -soon -sorry -sort -soul -sound -soup -source -south -space -spare -spatial -spawn -speak -special -speed -spell -spend -sphere -spice -spider -spike -spin -spirit -split -spoil -sponsor -spoon -sport -spot -spray -spread -spring -spy -square -squeeze -squirrel -stable -stadium -staff -stage -stairs -stamp -stand -start -state -stay -steak -steel -stem -step -stereo -stick -still -sting -stock -stomach -stone -stool -story -stove -strategy -street -strike -strong -struggle -student -stuff -stumble -style -subject -submit -subway -success -such -sudden -suffer -sugar -suggest -suit -summer -sun -sunny -sunset -super -supply -supreme -sure -surface -surge -surprise -surround -survey -suspect -sustain -swallow -swamp -swap -swarm -swear -sweet -swift -swim -swing -switch -sword -symbol -symptom -syrup -system -table -tackle -tag -tail -talent -talk -tank -tape -target -task -taste -tattoo -taxi -teach -team -tell -ten -tenant -tennis -tent -term -test -text -thank -that -theme -then -theory -there -they -thing -this -thought -three -thrive -throw -thumb -thunder -ticket -tide -tiger -tilt -timber -time -tiny -tip -tired -tissue -title -toast -tobacco -today -toddler -toe -together -toilet -token -tomato -tomorrow -tone -tongue -tonight -tool -tooth -top -topic -topple -torch -tornado -tortoise -toss -total -tourist -toward -tower -town -toy -track -trade -traffic -tragic -train -transfer -trap -trash -travel -tray -treat -tree -trend -trial -tribe -trick -trigger -trim -trip -trophy -trouble -truck -true -truly -trumpet -trust -truth -try -tube -tuition -tumble -tuna -tunnel -turkey -turn -turtle -twelve -twenty -twice -twin -twist -two -type -typical -ugly -umbrella -unable -unaware -uncle -uncover -under -undo -unfair -unfold -unhappy -uniform -unique -unit -universe -unknown -unlock -until -unusual -unveil -update -upgrade -uphold -upon -upper -upset -urban -urge -usage -use -used -useful -useless -usual -utility -vacant -vacuum -vague -valid -valley -valve -van -vanish -vapor -various -vast -vault -vehicle -velvet -vendor -venture -venue -verb -verify -version -very -vessel -veteran -viable -vibrant -vicious -victory -video -view -village -vintage -violin -virtual -virus -visa -visit -visual -vital -vivid -vocal -voice -void -volcano -volume -vote -voyage -wage -wagon -wait -walk -wall -walnut -want -warfare -warm -warrior -wash -wasp -waste -water -wave -way -wealth -weapon -wear -weasel -weather -web -wedding -weekend -weird -welcome -west -wet -whale -what -wheat -wheel -when -where -whip -whisper -wide -width -wife -wild -will -win -window -wine -wing -wink -winner -winter -wire -wisdom -wise -wish -witness -wolf -woman -wonder -wood -wool -word -work -world -worry -worth -wrap -wreck -wrestle -wrist -write -wrong -yard -year -yellow -you -young -youth -zebra -zero -zone -zoo diff --git a/v1_old_files/requirements-ml.txt b/v1_old_files/requirements-ml.txt deleted file mode 100644 index 0933ed8..0000000 --- a/v1_old_files/requirements-ml.txt +++ /dev/null @@ -1,8 +0,0 @@ -# ML dependencies for AI-powered story generation -# Install with: pip install -r requirements-ml.txt -# -# Note: These add ~1-2GB disk space for model downloads -# The app works without these (falls back to template-based stories) - -transformers>=4.35.0 -torch>=2.0.0 diff --git a/v1_old_files/requirements.txt b/v1_old_files/requirements.txt deleted file mode 100644 index 9605ef7..0000000 --- a/v1_old_files/requirements.txt +++ /dev/null @@ -1,16 +0,0 @@ -# Core dependencies -flask>=3.0.0 -gunicorn>=21.0.0 -pillow>=10.0.0 -cryptography>=41.0.0 - -# Memory-hard key derivation (highly recommended) -argon2-cffi>=23.0.0 - -# Optional: ML story generation (adds ~1GB disk space) -# Uncomment for AI-powered memory aid stories -# transformers>=4.35.0 -# torch>=2.0.0 - -# Optional: For production deployment -# gevent>=23.0.0 diff --git a/v1_old_files/secureDeleter.py b/v1_old_files/secureDeleter.py deleted file mode 100644 index 7956f9b..0000000 --- a/v1_old_files/secureDeleter.py +++ /dev/null @@ -1,65 +0,0 @@ -import os -import shutil -import random - -class SecureDeleter: - def __init__(self, path): - self.path = path - - def overwrite_file(self, file_path, passes=7): - """Overwrites the file with multiple patterns and random data.""" - patterns = [b'\x00', b'\xFF', random.randbytes(1)] - print(patterns) - length = os.path.getsize(file_path) - for _ in range(passes): - with open(file_path, "r+b") as file: - for pattern in patterns: - file.seek(0) - for _ in range(length): - file.write(pattern) - # Final pass with random data for each byte - file.seek(0) - file.write(random.randbytes(length)) - - def delete_file(self, file_path): - """Securely deletes a file.""" - if os.path.isfile(file_path): - self.overwrite_file(file_path) - os.remove(file_path) - - def delete_folder(self, folder_path): - """Securely deletes a folder and its contents.""" - if os.path.isdir(folder_path): - for root, dirs, files in os.walk(folder_path, topdown=False): - for name in files: - file_path = os.path.join(root, name) - self.delete_file(file_path) - for name in dirs: - os.rmdir(os.path.join(root, name)) - shutil.rmtree(folder_path)\ - - def wipe_free_space(self, drive, size=1024*1024*10): # 10MB default file size - """Writes temporary files with random data to overwrite free disk space.""" - temp_files = [] - try: - while True: - temp_file = os.path.join(drive, f"temp_{random.randint(0, 999999)}.dat") - with open(temp_file, "wb") as file: - file.write(random.randbytes(size)) - temp_files.append(temp_file) - except OSError: # Typically disk full - for temp_file in temp_files: - os.remove(temp_file) - - def execute(self): - """Determines whether the path is a file or folder and deletes it securely.""" - if os.path.isfile(self.path): - self.delete_file(self.path) - elif os.path.isdir(self.path): - self.delete_folder(self.path) - else: - print("Path does not exist.") - -# Usage example: -#deleter = SecureDeleter("/path/to/your/file_or_folder") -#deleter.execute() diff --git a/v1_old_files/static/.gitkeep b/v1_old_files/static/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/v1_old_files/static/favicon.svg b/v1_old_files/static/favicon.svg deleted file mode 100644 index 9b9ae19..0000000 --- a/v1_old_files/static/favicon.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/v1_old_files/static/logo.svg b/v1_old_files/static/logo.svg deleted file mode 100644 index 6c31290..0000000 --- a/v1_old_files/static/logo.svg +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/v1_old_files/static/style.css b/v1_old_files/static/style.css deleted file mode 100644 index 9f8a840..0000000 --- a/v1_old_files/static/style.css +++ /dev/null @@ -1,257 +0,0 @@ -/* ============================================================================ - Stegasoo - Main Stylesheet - ============================================================================ */ - -/* ---------------------------------------------------------------------------- - CSS Variables - ---------------------------------------------------------------------------- */ -:root { - --gradient-start: #667eea; - --gradient-end: #764ba2; - --bg-dark-1: #1a1a2e; - --bg-dark-2: #16213e; - --bg-dark-3: #0f3460; - --text-muted: rgba(255, 255, 255, 0.5); - --border-light: rgba(255, 255, 255, 0.1); - --overlay-dark: rgba(0, 0, 0, 0.3); - --overlay-light: rgba(255, 255, 255, 0.05); -} - -/* ---------------------------------------------------------------------------- - Base Styles - ---------------------------------------------------------------------------- */ -body { - min-height: 100vh; - background: linear-gradient(135deg, var(--bg-dark-1) 0%, var(--bg-dark-2) 50%, var(--bg-dark-3) 100%); -} - -/* ---------------------------------------------------------------------------- - Navigation - ---------------------------------------------------------------------------- */ -.navbar { - background: var(--overlay-dark) !important; - backdrop-filter: blur(10px); -} - -/* ---------------------------------------------------------------------------- - Cards - ---------------------------------------------------------------------------- */ -.card { - background: var(--overlay-light); - backdrop-filter: blur(10px); - border: 1px solid var(--border-light); -} - -.card-header { - background: linear-gradient(135deg, var(--gradient-start), var(--gradient-end)); - border-bottom: none; -} - -.feature-card { - transition: transform 0.3s ease, box-shadow 0.3s ease; -} - -.feature-card:hover { - transform: translateY(-5px); - box-shadow: 0 10px 40px rgba(102, 126, 234, 0.2); -} - -/* ---------------------------------------------------------------------------- - Buttons - ---------------------------------------------------------------------------- */ -.btn-primary { - background: linear-gradient(135deg, var(--gradient-start), var(--gradient-end)); - border: none; -} - -.btn-primary:hover { - background: linear-gradient(135deg, var(--gradient-end), var(--gradient-start)); - transform: translateY(-2px); - box-shadow: 0 5px 20px rgba(102, 126, 234, 0.4); -} - -/* ---------------------------------------------------------------------------- - Forms - ---------------------------------------------------------------------------- */ -.form-control, -.form-select { - background: var(--overlay-light); - border: 1px solid var(--border-light); - color: #fff; -} - -.form-control:focus, -.form-select:focus { - background: rgba(255, 255, 255, 0.1); - border-color: var(--gradient-start); - box-shadow: 0 0 0 0.25rem rgba(102, 126, 234, 0.25); - color: #fff; -} - -.form-control::placeholder { - color: var(--text-muted); -} - -/* Fix dropdown options for dark theme */ -.form-select option { - background: var(--bg-dark-1); - color: #fff; -} - -/* ---------------------------------------------------------------------------- - Hero & Icons - ---------------------------------------------------------------------------- */ -.hero-icon { - font-size: 4rem; - background: linear-gradient(135deg, var(--gradient-start), var(--gradient-end)); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; -} - -/* ---------------------------------------------------------------------------- - Phrase Display - ---------------------------------------------------------------------------- */ -.phrase-display { - font-family: 'Courier New', monospace; - font-size: 1rem; - background: var(--overlay-dark); - padding: 0.5rem 0.75rem; - border-radius: 0.5rem; - border-left: 4px solid var(--gradient-start); - display: inline-block; - line-height: 1.6; - word-spacing: 0.3rem; -} - -/* ---------------------------------------------------------------------------- - PIN Display - ---------------------------------------------------------------------------- */ -.pin-display { - font-family: 'Courier New', monospace; - font-size: 3rem; - font-weight: bold; - letter-spacing: 0.75rem; - background: linear-gradient(135deg, #fef08a, #fcd34d, #fb923c); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; - display: inline-block; - line-height: 1; -} - -.pin-container { - background: var(--overlay-dark); - border: 1px solid var(--border-light); - border-radius: 0.75rem; - padding: 1.5rem 2rem; - display: inline-block; -} - -/* ---------------------------------------------------------------------------- - Story Cards (Memory Aid) - ---------------------------------------------------------------------------- */ -.story-word { - color: #ff6b6b; - font-weight: bold; - text-transform: uppercase; -} - -.story-card { - background: rgba(0, 0, 0, 0.2); - border-left: 3px solid var(--gradient-start); - padding: 1rem; - margin-bottom: 0.75rem; - border-radius: 0.5rem; - font-size: 0.95rem; - line-height: 1.6; -} - -.story-card .day-label { - font-weight: bold; - color: var(--gradient-start); - margin-bottom: 0.5rem; -} - -/* ---------------------------------------------------------------------------- - Alert / Message Display - ---------------------------------------------------------------------------- */ -.alert-message { - background: var(--overlay-dark); - border: 1px solid var(--border-light); - border-radius: 0.5rem; - padding: 1.5rem; - white-space: pre-wrap; - font-family: 'Courier New', monospace; -} - -/* ---------------------------------------------------------------------------- - Drop Zone (Drag & Drop File Upload) - ---------------------------------------------------------------------------- */ -.drop-zone { - position: relative; - border: 2px dashed rgba(255, 255, 255, 0.2); - border-radius: 0.5rem; - padding: 1.5rem; - text-align: center; - transition: all 0.2s ease; -} - -.drop-zone.drag-over { - border-color: var(--gradient-start); - background: rgba(102, 126, 234, 0.1); -} - -.drop-zone input[type="file"] { - position: absolute; - inset: 0; - opacity: 0; - cursor: pointer; -} - -.drop-zone-label { - pointer-events: none; -} - -.drop-zone-preview { - max-height: 120px; - border-radius: 0.375rem; - margin-top: 0.75rem; -} - -/* ---------------------------------------------------------------------------- - Footer - ---------------------------------------------------------------------------- */ -footer { - background: rgba(0, 0, 0, 0.2); -} - -/* ---------------------------------------------------------------------------- - Custom Alert Variants - ---------------------------------------------------------------------------- */ -.alert-success-bright { - background: rgba(34, 197, 94, 0.2); - border-color: #22c55e; - color: #4ade80; -} - -/* ---------------------------------------------------------------------------- - Utility Classes - ---------------------------------------------------------------------------- */ -.bg-dark-subtle { - background: rgba(0, 0, 0, 0.2); -} - -.status-box { - background: rgba(0, 0, 0, 0.2); - padding: 1rem; - border-radius: 0.5rem; -} - -.result-icon { - font-size: 4rem; -} - -.footer-icon { - vertical-align: text-bottom; -} diff --git a/v1_old_files/templates/about.html b/v1_old_files/templates/about.html deleted file mode 100644 index 7ac6af0..0000000 --- a/v1_old_files/templates/about.html +++ /dev/null @@ -1,178 +0,0 @@ -{% extends "base.html" %} - -{% block title %}About - Stegasoo{% endblock %} - -{% block content %} -
-
-
-
-
About Stegasoo
-
-
-

- Stegasoo is a hybrid steganography system that hides encrypted messages inside - ordinary images. It combines multiple security layers to create a system that is - both highly secure and practical to use. -

- -
System Status
-
-
-
- {% if has_argon2 %} - -
- Argon2id Available -
Memory-hard key derivation (256MB)
-
- {% else %} - -
- Using PBKDF2 Fallback -
Install argon2-cffi for better security
-
- {% endif %} -
-
-
-
- -
- AES-256-GCM -
Authenticated encryption enabled
-
-
-
-
-
-
- -
-
-
Security Model
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ComponentEntropyPurpose
Reference Photo~80-256 bitsSomething you have (plausible deniability)
3-Word Phrase~33 bitsSomething you know (changes daily)
6-Digit PIN~20 bitsSomething you know (static)
DateN/AAutomatic key rotation
Combined133+ bitsBeyond brute force
-
-
-
- -
-
-
Attack Resistance
-
-
-
-
-
What Attackers Can't Do
-
    -
  • - - Brute force the passphrase (2133 combinations) -
  • -
  • - - Use rainbow tables (random salt per message) -
  • -
  • - - Detect hidden data (random pixel selection) -
  • -
  • - - Use GPU farms (Argon2 requires 256MB RAM per attempt) -
  • -
-
-
-
Real Threats
-
    -
  • - - Social engineering (someone tricks you) -
  • -
  • - - Physical access to your devices -
  • -
  • - - Malware/keyloggers on your system -
  • -
  • - - Shoulder surfing while you type -
  • -
-
-
-
-
- -
-
-
Best Practices
-
-
-
-
-
Do
-
    -
  • Memorize your phrases and PIN, never write them down
  • -
  • Use a reference photo that both parties already have
  • -
  • Use different carrier images for each message
  • -
  • Share stego images through normal channels (looks innocent)
  • -
-
-
-
Don't
-
    -
  • Don't transmit the reference photo
  • -
  • Don't reuse the same carrier image
  • -
  • Don't store phrases or PIN digitally
  • -
  • Don't resize or recompress stego images
  • -
-
-
-
-
-
-
-{% endblock %} diff --git a/v1_old_files/templates/base.html b/v1_old_files/templates/base.html deleted file mode 100644 index 7254369..0000000 --- a/v1_old_files/templates/base.html +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - {% block title %}Stegasoo{% endblock %} - - - - - - - - -
- {% with messages = get_flashed_messages(with_categories=true) %} - {% if messages %} - {% for category, message in messages %} - - {% endfor %} - {% endif %} - {% endwith %} - - {% block content %}{% endblock %} -
- -
-
- - - Stegasoo v1.1 — Hybrid Photo + Day-Phrase + PIN Steganography - -
-
- - - {% block scripts %}{% endblock %} - - diff --git a/v1_old_files/templates/decode.html b/v1_old_files/templates/decode.html deleted file mode 100644 index 226d6fd..0000000 --- a/v1_old_files/templates/decode.html +++ /dev/null @@ -1,259 +0,0 @@ -{% extends "base.html" %} - -{% block title %}Decode Message - Stegasoo{% endblock %} - -{% block content %} -
-
-
-
-
Decode Secret Message
-
-
- {% if decoded_message %} -
-
Message Decrypted Successfully!
-
- -
- -
{{ decoded_message }}
-
- - - Decode Another Message - - - {% else %} - -
-
-
- -
- -
- - Drop image or click to browse -
- -
-
- The same reference photo used for encoding -
-
- -
- -
- -
- - Drop image or click to browse -
- -
-
- The image containing the hidden message -
-
-
- -
- - -
- The phrase for the day the message was encoded -
-
- -
- -
- SECURITY FACTORS - (provide same factors used during encoding) -
- -
-
- - -
- If PIN was used during encoding -
-
- -
- - -
- If RSA key was used during encoding -
-
-
- - -
- - -
- Leave blank if your key file is not password-protected -
-
- - -
- - {% endif %} -
-
- -
-
-
Troubleshooting
-
    -
  • - - Make sure you're using the exact same reference photo file -
  • -
  • - - Use the phrase for the day the message was encoded, not today -
  • -
  • - - Provide the same security factors (PIN and/or RSA key) used during encoding -
  • -
  • - - Ensure the stego image hasn't been resized or recompressed -
  • -
  • - - If using an RSA key, make sure the password is correct -
  • -
-
-
-
-
-{% endblock %} - -{% block scripts %} - -{% endblock %} diff --git a/v1_old_files/templates/encode.html b/v1_old_files/templates/encode.html deleted file mode 100644 index 8498c19..0000000 --- a/v1_old_files/templates/encode.html +++ /dev/null @@ -1,259 +0,0 @@ -{% extends "base.html" %} - -{% block title %}Encode Message - Stegasoo{% endblock %} - -{% block content %} -
-
-
-
-
Encode Secret Message
-
-
-
- - -
-
- -
- -
- - Drop image or click to browse -
- -
-
- The secret photo both parties have (NOT transmitted) -
-
- -
- -
- -
- - Drop image or click to browse -
- -
-
- The image to hide your message in (e.g., a meme) -
-
-
- -
- - -
- - 0 / 50,000 characters - - Getting long! - - - 0% -
-
- -
- - -
- Your phrase for today (based on your local timezone) -
-
- -
- -
- SECURITY FACTORS - (provide at least one: PIN or RSA Key) -
- -
-
- - -
- Your static 6-9 digit PIN (if configured) -
-
- -
- - -
- Your shared .pem key file (if configured) -
-
-
- - -
- - -
- Leave blank if your key file is not password-protected -
-
- - -
- -
- -
-
- - AES-256-GCM Encryption -
-
- - Random Pixel Embedding -
-
- - Undetectable by Analysis -
-
- -
- - Limits: - Carrier image max ~4 megapixels (2000×2000). - Files max 5MB each. - Message max 50KB. -
-
-
-
-
-{% endblock %} - -{% block scripts %} - -{% endblock %} diff --git a/v1_old_files/templates/encode_result.html b/v1_old_files/templates/encode_result.html deleted file mode 100644 index 1837833..0000000 --- a/v1_old_files/templates/encode_result.html +++ /dev/null @@ -1,142 +0,0 @@ -{% extends "base.html" %} - -{% block title %}Message Encoded - Stegasoo{% endblock %} - -{% block content %} -
-
-
-
-
Message Encoded Successfully!
-
-
-
- -
{{ filename }}
-

Your secret message is hidden in this image

-
- -
- - Download Image - - - -
- - -
-

Share via:

- -
- -
- -
- - File expires in 5 minutes. - Download or share now. The file will be securely deleted after expiry. -
- - - Encode Another Message - -
-
-
-
-{% endblock %} - -{% block scripts %} - -{% endblock %} diff --git a/v1_old_files/templates/generate.html b/v1_old_files/templates/generate.html deleted file mode 100644 index 5d6af8d..0000000 --- a/v1_old_files/templates/generate.html +++ /dev/null @@ -1,345 +0,0 @@ -{% extends "base.html" %} - -{% block title %}Generate Credentials - Stegasoo{% endblock %} - -{% block content %} -
-
-
-
-
Generate Credentials
-
-
- {% if not generated %} -

- Generate your weekly phrase card and security factors. You must choose at least one: PIN or RSA Key. -

- -
-
- - -
More words = more security, harder to memorize
-
- -
- -
SECURITY FACTORS (select at least one)
- - -
-
-
- - -
-
- - -
Memorizable, same PIN used every day
-
-
-
- - -
-
-
- - -
-
- - -
File-based key, both parties need the same .pem file
-
-
-
- -
-
- Estimated entropy: - ~53 bits -
-
-
-
- - Good for most use cases - • Reference photo adds ~80-256 bits more - -
- -
- - You must select at least one security factor (PIN or RSA Key) -
- - -
- - {% else %} - - -
- - Credentials Generated! - -
- -
- - Memorize phrases, save key securely, then close! - Do not screenshot - -
- - {% if pin %} -
-
-
YOUR STATIC PIN
-
-
{{ pin }}
-
-
- Use this {{ pin_length }}-digit PIN every day -
-
- {% endif %} - - {% if rsa_key_pem %} -
-
-
- YOUR RSA KEY ({{ rsa_bits }}-bit) -
- -
- - Save this key securely! Share it with your recipient through a secure channel. You cannot recover it later. -
- - -
- -
- - - - - - - -
-
-
-
- -
- - -
You'll need this password when using the key
-
- -
-
-
-
-
- {% endif %} - -
- -
DAILY PHRASES ({{ words_per_phrase }} words each)
- -
- - - - - - - - - {% for day in days %} - - - - - {% endfor %} - -
DayPhrase
- {{ day }} - - {{ phrases[day] }} -
-
- -
-
Security Summary
-
-
-
{{ phrase_entropy }}
- bits/phrase -
- {% if pin %} -
-
{{ pin_entropy }}
- bits/PIN -
- {% endif %} - {% if rsa_key_pem %} -
-
{{ rsa_entropy }}
- bits/RSA -
- {% endif %} -
-
{{ total_entropy }}
- bits total -
-
- - + reference photo (~80-256 bits) = {{ total_entropy + 80 }}+ bits combined - -
- - - Generate New Credentials - - - {% endif %} -
-
-
-
-{% endblock %} - -{% block scripts %} - -{% endblock %} diff --git a/v1_old_files/templates/index.html b/v1_old_files/templates/index.html deleted file mode 100644 index 19deb0e..0000000 --- a/v1_old_files/templates/index.html +++ /dev/null @@ -1,108 +0,0 @@ -{% extends "base.html" %} - -{% block title %}Stegasoo - Secure Steganography{% endblock %} - -{% block content %} -
- Stegasoo -

Stegasoo

-

Create hidden encrypted messages in images and photos using advanced steganography.

-
-
- -
-
-
- -
-
-
Encode Message
-

- Hide your secret message inside an innocent-looking image using your daily phrase + PIN. -

- - Encode - -
-
-
- -
-
-
- -
-
-
Decode Message
-

- Extract and decrypt hidden messages from Stegasoo-encoded images using your credentials. -

- - Decode - -
-
-
- -
-
-
- -
-
-
Generate Keys
-

- Create your weekly phrase card and PIN. Memorize 21 words + 6 digits for maximum security. -

- - Generate - -
-
-
-
- -
-
-
How It Works
-
-
-
-
-
Key Components
-
    -
  • - - Reference Photo — Any photo you and recipient both have -
  • -
  • - - Day Phrase — 3 words, different each day of the week -
  • -
  • - - Static PIN — 6 digits, same every day -
  • -
-
-
-
Security Features
-
    -
  • - - Argon2id memory-hard key derivation (256MB) -
  • -
  • - - Pseudo-random pixel selection (defeats steganalysis) -
  • -
  • - - AES-256-GCM authenticated encryption -
  • -
-
-
-
-
-{% endblock %} diff --git a/v1_old_files/test/story_generator.py b/v1_old_files/test/story_generator.py deleted file mode 100644 index a7a10b6..0000000 --- a/v1_old_files/test/story_generator.py +++ /dev/null @@ -1,244 +0,0 @@ -""" -Story Generator for Passphrase Memorization -Uses lightweight ML (DistilGPT-2) for coherent stories, with template fallback. -""" - -import random -import re -from typing import Optional - -# Try to import ML libraries -try: - from transformers import pipeline, set_seed - import torch - HAS_ML = True -except ImportError: - HAS_ML = False - -# Global generator (lazy loaded) -_generator = None -_model_loaded = False - - -def get_generator(): - """Lazy load the text generation model.""" - global _generator, _model_loaded - - if not HAS_ML: - return None - - if not _model_loaded: - try: - # Use distilgpt2 - small (~350MB) and fast - device = 0 if torch.cuda.is_available() else -1 - _generator = pipeline( - 'text-generation', - model='distilgpt2', - device=device, - torch_dtype=torch.float32 - ) - _model_loaded = True - print("ML story generator loaded successfully") - except Exception as e: - print(f"Could not load ML model: {e}. Using templates.") - _generator = None - _model_loaded = True # Don't retry - - return _generator - - -def generate_story_ml(day: str, words: list[str], max_attempts: int = 3) -> Optional[str]: - """ - Generate a story using ML that incorporates all passphrase words. - - Returns None if ML is unavailable or generation fails. - """ - generator = get_generator() - if generator is None: - return None - - # Create a compelling prompt - words_str = ', '.join(words[:-1]) + f', and {words[-1]}' if len(words) > 1 else words[0] - - prompts = [ - f"{day}, something memorable happened including: {words_str}.", - ] - - prompt = random.choice(prompts) - - try: - set_seed(random.randint(0, 10000)) - - # Generate text - result = generator( - prompt, - max_new_tokens=80, - num_return_sequences=1, - temperature=0.8, - top_p=0.9, - do_sample=True, - pad_token_id=50256, # eos token for gpt2 - ) - - story = result[0]['generated_text'] - - # Clean up - get just a few sentences - story = story.strip() - - # Try to end at a sentence boundary - for end_char in ['. ', '! ', '? ']: - last_end = story.rfind(end_char) - if last_end > len(prompt) + 20: - story = story[:last_end + 1] - break - - # Verify most words are present (ML doesn't always include all) - story_lower = story.lower() - words_found = sum(1 for w in words if w.lower() in story_lower) - - if words_found < len(words) * 0.5: # At least 50% of words - # Append missing words naturally - missing = [w for w in words if w.lower() not in story_lower] - if missing: - story += f" Don't forget: {', '.join(missing)}." - - return story - - except Exception as e: - print(f"ML generation error: {e}") - return None - - -# ============================================================================ -# TEMPLATE FALLBACK (always available) -# ============================================================================ - -STORY_TEMPLATES = { - 'Monday': [ - "Monday morning began when I discovered a {0} near the {1}. I had to {2} quickly, then grab the {3} before reaching the {4}.", - "The week started with a {0} appearing at the {1}. My plan was to {2}, secure the {3}, and head toward the {4}.", - "On Monday, the {0} and the {1} crossed paths. We decided to {2}, bring the {3}, and meet at the {4}.", - ], - 'Tuesday': [ - "Tuesday brought a {0} to the {1}. Everyone wanted to {2}, especially with the {3} near the {4}.", - "The {0} arrived Tuesday carrying a {1}. Together we would {2}, protect the {3}, and explore the {4}.", - "On Tuesday, my {0} transformed into a {1}. I needed to {2}, find the {3}, and unlock the {4}.", - ], - 'Wednesday': [ - "By Wednesday, the {0} had found a {1}. The mission: {2}, retrieve the {3}, and guard the {4}.", - "Midweek magic: a {0} emerged from the {1}. We had to {2}, grab the {3}, and escape to the {4}.", - "Wednesday's {0} was hiding near the {1}. To {2} successfully, we needed the {3} and the {4}.", - ], - 'Thursday': [ - "Thursday's {0} came with a {1}. Our plan: {2}, then move the {3} inside the {4}.", - "On Thursday, the {0} met the {1} unexpectedly. They decided to {2}, share the {3}, and visit the {4}.", - "The {0} adventure on Thursday led us to a {1}. We chose to {2}, carry the {3}, and discover the {4}.", - ], - 'Friday': [ - "Friday arrived with a {0} and a {1}. Time to {2}, celebrate with the {3}, and toast the {4}!", - "TGIF! The {0} party featured a {1}. We would {2}, enjoy the {3}, and dance around the {4}.", - "Friday's surprise was a {0} inside a {1}. Everyone wanted to {2}, taste the {3}, and admire the {4}.", - ], - 'Saturday': [ - "Saturday morning, the {0} journeyed to the {1}. Goals: {2}, collect the {3}, and protect the {4}.", - "Weekend mode: a {0} relaxing near a {1}. I chose to {2}, photograph the {3}, and sketch the {4}.", - "On Saturday, the legendary {0} appeared at the {1}. Heroes must {2}, wield the {3}, and defeat the {4}.", - ], - 'Sunday': [ - "Sunday peace was broken by a {0} and a {1}. We needed to {2}, fix the {3}, and restore the {4}.", - "A quiet Sunday with my {0} near the {1}. Plans: {2} later, maybe find the {3}, or visit the {4}.", - "Sunday sunset revealed a {0} beside a {1}. Time to {2}, remember the {3}, and dream of the {4}.", - ], -} - -# Extensions for 6+ word phrases -EXTENSIONS = [ - [" Suddenly, a {5} appeared!"], - [" The {6} changed everything."], - [" Behind it was a {7}."], - [" Plus a mysterious {8}."], - [" The {9} completed the quest."], - [" A {10} watched from afar."], - [" And finally, the legendary {11}."], -] - - -def generate_story_template(day: str, words: list[str]) -> str: - """Generate story using templates (fallback method).""" - templates = STORY_TEMPLATES.get(day, STORY_TEMPLATES['Monday']) - template = random.choice(templates) - - # Add extensions for longer phrases - for i, ext_list in enumerate(EXTENSIONS): - word_idx = i + 5 - if len(words) > word_idx: - template += random.choice(ext_list) - - # Pad words list to ensure we have enough for any template - padded_words = words + [''] * (12 - len(words)) - - return template.format(*padded_words) - - -# ============================================================================ -# MAIN API -# ============================================================================ - -def generate_story(day: str, words: list[str], use_ml: bool = True) -> dict: - """ - Generate a memorable story incorporating the passphrase words. - - Args: - day: Day of the week (e.g., 'Monday') - words: List of passphrase words - use_ml: Whether to try ML generation first - - Returns: - dict with 'story' (plain text) and 'story_html' (with highlighted words) - """ - story = None - used_ml = False - - # Try ML first if requested - if use_ml and HAS_ML: - story = generate_story_ml(day, words) - if story: - used_ml = True - - # Fall back to templates - if story is None: - story = generate_story_template(day, words) - - # Generate HTML version with highlighted words (RED and CAPS) - html_story = story - for word in words: - # Case-insensitive replacement with highlighted version - pattern = re.compile(re.escape(word), re.IGNORECASE) - html_story = pattern.sub( - f'{word.upper()}', - html_story - ) - - return { - 'story': story, - 'story_html': html_story, - 'used_ml': used_ml - } - - -def generate_all_stories(phrases: dict[str, str], use_ml: bool = True) -> dict[str, dict]: - """ - Generate stories for all days. - - Args: - phrases: Dict mapping day names to phrase strings - use_ml: Whether to use ML generation - - Returns: - Dict mapping day names to story dicts - """ - stories = {} - for day, phrase in phrases.items(): - words = phrase.split() - stories[day] = generate_story(day, words, use_ml=use_ml) - return stories diff --git a/v1_old_files/uploads/.gitkeep b/v1_old_files/uploads/.gitkeep deleted file mode 100644 index e69de29..0000000