From 11fc8aab2735eb19da2a0f3662b621fe60094e19 Mon Sep 17 00:00:00 2001 From: "Aaron D. Lee" Date: Thu, 1 Jan 2026 03:14:27 -0500 Subject: [PATCH] 3.2.0 Big revamp --- .../web/WEB_FRONTEND_UPDATE_SUMMARY_V3.2.0.md | 426 ++++++++++++++++++ frontends/web/app.py | 159 +++---- frontends/web/templates/about.html | 49 +- frontends/web/templates/encode.html | 79 ++-- frontends/web/templates/encode_result.html | 6 +- frontends/web/templates/generate.html | 272 +++++++++-- frontends/web/templates/index.html | 12 +- 7 files changed, 829 insertions(+), 174 deletions(-) create mode 100644 frontends/web/WEB_FRONTEND_UPDATE_SUMMARY_V3.2.0.md diff --git a/frontends/web/WEB_FRONTEND_UPDATE_SUMMARY_V3.2.0.md b/frontends/web/WEB_FRONTEND_UPDATE_SUMMARY_V3.2.0.md new file mode 100644 index 0000000..a7e5a95 --- /dev/null +++ b/frontends/web/WEB_FRONTEND_UPDATE_SUMMARY_V3.2.0.md @@ -0,0 +1,426 @@ +# Web Frontend Update Summary for v3.2.0 + +## Overview + +The Flask web frontend has been updated to align with Stegasoo v3.2.0's breaking changes: +1. **Removed date dependency** - No date selection or tracking in UI +2. **Renamed day_phrase → passphrase** - Updated all forms and templates +3. **Increased default words** - From 3 to 4 for better security + +## Key Changes + +### 1. Form Parameter Changes + +#### Generate Page + +**Before (v3.1.0):** +```python +words_per_phrase = int(request.form.get('words_per_phrase', 3)) +# Generated daily phrases for all days of the week +``` + +**After (v3.2.0):** +```python +words_per_passphrase = int(request.form.get('words_per_passphrase', 4)) +# Generates single passphrase +``` + +**Template variables changed:** +- `phrases` → `passphrase` (single string instead of dict) +- `words_per_phrase` → `words_per_passphrase` +- `phrase_entropy` → `passphrase_entropy` +- Removed `days` variable (no longer needed) + +#### Encode Page + +**Before (v3.1.0):** +```python +day_phrase = request.form.get('day_phrase', '') +client_date = request.form.get('client_date', '').strip() +day_of_week = get_today_day() # Used in template + +encode_result = encode( + ..., + day_phrase=day_phrase, + date_str=date_str, + ... +) +``` + +**After (v3.2.0):** +```python +passphrase = request.form.get('passphrase', '') +# No client_date or day_of_week needed + +encode_result = encode( + ..., + passphrase=passphrase, # Renamed + # date_str removed + ... +) +``` + +#### Decode Page + +**Before (v3.1.0):** +```python +day_phrase = request.form.get('day_phrase', '') +stego_date = request.form.get('stego_date', '').strip() + +decode_result = decode( + ..., + day_phrase=day_phrase, + date_str=stego_date if stego_date else None, + ... +) +``` + +**After (v3.2.0):** +```python +passphrase = request.form.get('passphrase', '') +# No stego_date needed + +decode_result = decode( + ..., + passphrase=passphrase, # Renamed + # date_str removed + ... +) +``` + +### 2. Template Context Updates + +**inject_globals() changes:** + +**Added:** +```python +'min_passphrase_words': MIN_PASSPHRASE_WORDS, +'recommended_passphrase_words': RECOMMENDED_PASSPHRASE_WORDS, +'default_passphrase_words': DEFAULT_PASSPHRASE_WORDS, +``` + +**Used for:** +- Showing passphrase length requirements +- Default values in generate form +- Validation messages + +### 3. Validation Updates + +**Added passphrase validation:** +```python +from stegasoo import validate_passphrase + +# In encode_page() +result = validate_passphrase(passphrase) +if not result.is_valid: + flash(result.error_message, 'error') + return ... + +# Show warning if passphrase is short +if result.warning: + flash(result.warning, 'warning') +``` + +### 4. Error Message Updates + +**Before:** +```python +flash('Day phrase is required', 'error') +flash('Decryption failed. Check your phrase, PIN...', 'error') +``` + +**After:** +```python +flash('Passphrase is required', 'error') +flash('Decryption failed. Check your passphrase, PIN...', 'error') +``` + +## Template Changes Needed + +These Flask routes will need corresponding template updates: + +### generate.html + +**Changes needed:** +```html + + + + +{% if generated %} +

Daily Phrases

+ {% for day in days %} + + {{ day }} + {{ phrases[day] }} + + {% endfor %} +{% endif %} + + + + + +{% if generated %} +

Passphrase

+
+ {{ passphrase }} +

Use this passphrase to encode and decode messages (no date needed!)

+
+{% endif %} +``` + +**Entropy display:** +```html + +
  • Phrase entropy: {{ phrase_entropy }} bits
  • + + +
  • Passphrase entropy: {{ passphrase_entropy }} bits ({{ words_per_passphrase }} words)
  • +``` + +### encode.html + +**Changes needed:** +```html + + + + + + +

    Defaults to today: {{ day_of_week }}

    + + + + +

    + v3.2.0: No date needed! Use your passphrase anytime. +

    +``` + +### decode.html + +**Changes needed:** +```html + + + + + + +

    Will be auto-detected from filename if possible

    + + + + + + +

    + v3.2.0: No date needed to decode! +

    + + +``` + +### index.html + +**Changes needed:** +```html + +

    Generate daily passphrases and security credentials

    +

    Hide messages using day-specific phrases

    + + +

    Generate passphrases and security credentials

    +

    v3.2.0: Simplified - no more daily rotation!

    +``` + +### about.html + +**Add v3.2.0 section:** +```html +

    Version 3.2.0 Changes

    + +``` + +## JavaScript Changes Needed + +### Remove date-related code: + +```javascript +// REMOVE THIS (date detection from filename) +function detectDateFromFilename(filename) { + const match = filename.match(/_(\d{4})(\d{2})(\d{2})/); + if (match) { + return `${match[1]}-${match[2]}-${match[3]}`; + } + return null; +} + +// REMOVE THIS (day-of-week display) +function updateDayOfWeek() { + const dateInput = document.getElementById('client_date'); + const dayDisplay = document.getElementById('day_display'); + // ... +} +``` + +### Update validation: + +```javascript +// Before +const dayPhrase = document.getElementById('day_phrase').value; +if (!dayPhrase || dayPhrase.trim().length === 0) { + alert('Day phrase is required'); + return false; +} + +// After +const passphrase = document.getElementById('passphrase').value; +if (!passphrase || passphrase.trim().length === 0) { + alert('Passphrase is required'); + return false; +} + +// Add word count validation +const words = passphrase.trim().split(/\s+/); +if (words.length < {{ min_passphrase_words }}) { + alert(`Passphrase should have at least {{ recommended_passphrase_words }} words`); + return false; +} +``` + +## CSS Updates + +Add styling for passphrase warnings: + +```css +.passphrase-display { + background: #f5f5f5; + padding: 15px; + border-radius: 5px; + margin: 10px 0; +} + +.passphrase-display code { + font-size: 1.2em; + color: #2c3e50; + word-break: break-word; +} + +.help-text.v3-2-0 { + color: #3498db; + font-weight: bold; +} + +.flash.warning { + background-color: #fff3cd; + border-left: 4px solid #ffc107; + color: #856404; +} +``` + +## Migration Notes for Users + +Add to templates: + +```html +
    +

    ⚠️ v3.2.0 Breaking Changes

    +

    If you have messages encoded with v3.1.0:

    + +
    +``` + +## Form Field Summary + +### Changed Field Names + +| Old Name (v3.1.0) | New Name (v3.2.0) | Type | +|-------------------|-------------------|------| +| `day_phrase` | `passphrase` | text input | +| `words_per_phrase` | `words_per_passphrase` | number input | +| `client_date` | (removed) | date input | +| `stego_date` | (removed) | date input | + +### New Validation Attributes + +```html + +``` + +## Testing Checklist + +- [ ] Generate page creates single passphrase +- [ ] Generate page shows correct entropy (4 words = 44 bits) +- [ ] Generate page doesn't show day names +- [ ] Encode page accepts passphrase (not day_phrase) +- [ ] Encode page doesn't have date selection +- [ ] Encode page shows v3.2.0 help text +- [ ] Decode page accepts passphrase +- [ ] Decode page doesn't have date input +- [ ] Decode page doesn't auto-detect date from filename +- [ ] Error messages say "passphrase" not "day phrase" +- [ ] Validation shows warnings for short passphrases +- [ ] QR code functionality still works +- [ ] DCT mode options still work +- [ ] All flash messages updated + +## Implementation Status + +✅ Flask routes updated +✅ Form parameter names changed +✅ Function calls updated +✅ Validation added for passphrases +✅ Error messages updated +✅ Template context updated +⏳ Templates need updating (generate.html, encode.html, decode.html, index.html, about.html) +⏳ JavaScript needs updating +⏳ CSS styling for v3.2.0 features + +## Quick Reference + +**To test the Flask app:** +```bash +cd frontends/web +python app.py +# Visit http://localhost:5000 +``` + +**Key user-facing changes:** +1. Generate: Shows one passphrase, not 7 daily phrases +2. Encode: No date selection, just passphrase +3. Decode: No date needed, just passphrase + +**Benefits to highlight:** +- ✅ Simpler UI (fewer fields) +- ✅ No date tracking needed +- ✅ Encode today, decode anytime +- ✅ Perfect for asynchronous communications diff --git a/frontends/web/app.py b/frontends/web/app.py index 45295c2..4f0e3fd 100644 --- a/frontends/web/app.py +++ b/frontends/web/app.py @@ -1,9 +1,16 @@ #!/usr/bin/env python3 """ -Stegasoo Web Frontend (v3.0.1) +Stegasoo Web Frontend (v3.2.0) Flask-based web UI for steganography operations. Supports both text messages and file embedding. + +CHANGES in v3.2.0: +- Removed date dependency from all operations +- Renamed day_phrase → passphrase +- No date selection or tracking needed +- Simplified user experience for asynchronous communications + NEW in v3.0: LSB and DCT embedding modes with advanced options. NEW in v3.0.1: DCT output format selection (PNG or JPEG) and color mode (grayscale or color). """ @@ -31,13 +38,12 @@ from stegasoo import ( export_rsa_key_pem, load_rsa_key, validate_pin, validate_message, validate_image, validate_rsa_key, validate_security_factors, - validate_file_payload, - get_today_day, generate_filename, - DAY_NAMES, + validate_file_payload, validate_passphrase, + generate_filename, StegasooError, DecryptionError, CapacityError, has_argon2, FilePayload, - # NEW in v3.0 - Embedding modes + # Embedding modes EMBED_MODE_LSB, EMBED_MODE_DCT, EMBED_MODE_AUTO, @@ -49,6 +55,8 @@ from stegasoo.constants import ( __version__, MAX_MESSAGE_SIZE, MAX_MESSAGE_CHARS, MIN_PIN_LENGTH, MAX_PIN_LENGTH, + MIN_PASSPHRASE_WORDS, RECOMMENDED_PASSPHRASE_WORDS, + DEFAULT_PASSPHRASE_WORDS, VALID_RSA_SIZES, MAX_FILE_SIZE, MAX_FILE_PAYLOAD_SIZE, MAX_UPLOAD_SIZE, TEMP_FILE_EXPIRY, TEMP_FILE_EXPIRY_MINUTES, @@ -111,26 +119,28 @@ def inject_globals(): 'temp_file_expiry_minutes': TEMP_FILE_EXPIRY_MINUTES, 'min_pin_length': MIN_PIN_LENGTH, 'max_pin_length': MAX_PIN_LENGTH, + # NEW in v3.2.0 + 'min_passphrase_words': MIN_PASSPHRASE_WORDS, + 'recommended_passphrase_words': RECOMMENDED_PASSPHRASE_WORDS, + 'default_passphrase_words': DEFAULT_PASSPHRASE_WORDS, # NEW in v3.0 'has_dct': has_dct_support(), } # ============================================================================ -# CONFIGURATION (for override attempts - currently using constants.py values) +# CONFIGURATION # ============================================================================ -# Try to import and override stegasoo constants if possible try: - # Check current limits - print(f"Current MAX_FILE_SIZE from constants: {MAX_FILE_SIZE}") + print(f"Stegasoo v{__version__} - Web Frontend") + print(f"Current MAX_FILE_SIZE: {MAX_FILE_SIZE}") print(f"Current MAX_FILE_PAYLOAD_SIZE: {MAX_FILE_PAYLOAD_SIZE}") - print(f"DCT support available: {has_dct_support()}") + print(f"DCT support: {has_dct_support()}") + print(f"QR code support: write={HAS_QRCODE}, read={HAS_QRCODE_READ}") DESIRED_PAYLOAD_SIZE = 2 * 1024 * 1024 # 2MB - # Note: You might need to patch the stegasoo module - # if MAX_FILE_PAYLOAD_SIZE is used internally if hasattr(stegasoo, 'MAX_FILE_PAYLOAD_SIZE'): print(f"Overriding MAX_FILE_PAYLOAD_SIZE to {DESIRED_PAYLOAD_SIZE}") stegasoo.MAX_FILE_PAYLOAD_SIZE = DESIRED_PAYLOAD_SIZE @@ -165,7 +175,6 @@ def generate_thumbnail(image_data: bytes, size: tuple = THUMBNAIL_SIZE) -> bytes img.save(buffer, format='JPEG', quality=THUMBNAIL_QUALITY, optimize=True) return buffer.getvalue() except Exception as e: - # Log error but don't crash print(f"Thumbnail generation error: {e}") return None @@ -212,7 +221,8 @@ def index(): @app.route('/generate', methods=['GET', 'POST']) def generate(): if request.method == 'POST': - words_per_phrase = int(request.form.get('words_per_phrase', 3)) + # v3.2.0: Changed from words_per_phrase to words_per_passphrase, default increased to 4 + words_per_passphrase = int(request.form.get('words_per_passphrase', DEFAULT_PASSPHRASE_WORDS)) use_pin = request.form.get('use_pin') == 'on' use_rsa = request.form.get('use_rsa') == 'on' @@ -224,7 +234,7 @@ def generate(): rsa_bits = int(request.form.get('rsa_bits', 2048)) # Clamp values - words_per_phrase = max(3, min(12, words_per_phrase)) + words_per_passphrase = max(MIN_PASSPHRASE_WORDS, min(12, words_per_passphrase)) pin_length = max(MIN_PIN_LENGTH, min(MAX_PIN_LENGTH, pin_length)) if rsa_bits not in VALID_RSA_SIZES: rsa_bits = 2048 @@ -235,7 +245,7 @@ def generate(): use_rsa=use_rsa, pin_length=pin_length, rsa_bits=rsa_bits, - words_per_phrase=words_per_phrase + words_per_passphrase=words_per_passphrase ) # Store RSA key temporarily for QR generation @@ -261,18 +271,18 @@ def generate(): 'compress': qr_needs_compression } + # v3.2.0: Single passphrase instead of daily phrases return render_template('generate.html', - phrases=creds.phrases, + passphrase=creds.passphrase, # v3.2.0: Single passphrase pin=creds.pin, - days=DAY_NAMES, generated=True, - words_per_phrase=words_per_phrase, + words_per_passphrase=words_per_passphrase, pin_length=pin_length if use_pin else None, use_pin=use_pin, use_rsa=use_rsa, rsa_bits=rsa_bits, rsa_key_pem=creds.rsa_key_pem, - phrase_entropy=creds.phrase_entropy, + passphrase_entropy=creds.passphrase_entropy, pin_entropy=creds.pin_entropy, rsa_entropy=creds.rsa_entropy, total_entropy=creds.total_entropy, @@ -497,8 +507,6 @@ def api_check_fit(): @app.route('/encode', methods=['GET', 'POST']) def encode_page(): - day_of_week = get_today_day() - if request.method == 'POST': try: # Get files @@ -509,15 +517,15 @@ def encode_page(): 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, has_qrcode_read=HAS_QRCODE_READ) + return render_template('encode.html', has_qrcode_read=HAS_QRCODE_READ) if not allowed_image(ref_photo.filename) or not allowed_image(carrier.filename): flash('Invalid file type. Use PNG, JPG, or BMP', 'error') - return render_template('encode.html', day_of_week=day_of_week, has_qrcode_read=HAS_QRCODE_READ) + return render_template('encode.html', has_qrcode_read=HAS_QRCODE_READ) - # Get form data + # Get form data - v3.2.0: renamed from day_phrase to passphrase message = request.form.get('message', '') - day_phrase = request.form.get('day_phrase', '') + passphrase = request.form.get('passphrase', '') # v3.2.0: Renamed pin = request.form.get('pin', '').strip() rsa_password = request.form.get('rsa_password', '') payload_type = request.form.get('payload_type', 'text') @@ -532,7 +540,7 @@ def encode_page(): if dct_output_format not in ('png', 'jpeg'): dct_output_format = 'png' - # NEW in v3.0.1 - DCT color mode (default to 'color') + # NEW in v3.0.1 - DCT color mode dct_color_mode = request.form.get('dct_color_mode', 'color') if dct_color_mode not in ('grayscale', 'color'): dct_color_mode = 'color' @@ -540,7 +548,7 @@ def encode_page(): # Check DCT availability if embed_mode == 'dct' and not has_dct_support(): flash('DCT mode requires scipy. Install with: pip install scipy', 'error') - return render_template('encode.html', day_of_week=day_of_week, has_qrcode_read=HAS_QRCODE_READ) + return render_template('encode.html', has_qrcode_read=HAS_QRCODE_READ) # Determine payload if payload_type == 'file' and payload_file and payload_file.filename: @@ -550,7 +558,7 @@ def encode_page(): result = validate_file_payload(file_data, payload_file.filename) if not result.is_valid: flash(result.error_message, 'error') - return render_template('encode.html', day_of_week=day_of_week, has_qrcode_read=HAS_QRCODE_READ) + return render_template('encode.html', has_qrcode_read=HAS_QRCODE_READ) mime_type, _ = mimetypes.guess_type(payload_file.filename) payload = FilePayload( @@ -563,12 +571,23 @@ def encode_page(): result = validate_message(message) if not result.is_valid: flash(result.error_message, 'error') - return render_template('encode.html', day_of_week=day_of_week, has_qrcode_read=HAS_QRCODE_READ) + return render_template('encode.html', has_qrcode_read=HAS_QRCODE_READ) payload = message - if not day_phrase: - flash('Day phrase is required', 'error') - return render_template('encode.html', day_of_week=day_of_week, has_qrcode_read=HAS_QRCODE_READ) + # v3.2.0: Renamed from day_phrase + if not passphrase: + flash('Passphrase is required', 'error') + return render_template('encode.html', has_qrcode_read=HAS_QRCODE_READ) + + # v3.2.0: Validate passphrase + result = validate_passphrase(passphrase) + if not result.is_valid: + flash(result.error_message, 'error') + return render_template('encode.html', has_qrcode_read=HAS_QRCODE_READ) + + # Show warning if passphrase is short + if result.warning: + flash(result.warning, 'warning') # Read files ref_data = ref_photo.read() @@ -577,36 +596,34 @@ def encode_page(): # Handle RSA key - can come from .pem file or QR code image rsa_key_data = None rsa_key_qr = request.files.get('rsa_key_qr') - rsa_key_from_qr = False # Track source for password handling + rsa_key_from_qr = False if rsa_key_file and rsa_key_file.filename: - # RSA key from .pem file rsa_key_data = rsa_key_file.read() elif rsa_key_qr and rsa_key_qr.filename and HAS_QRCODE_READ: - # RSA key from QR code image qr_image_data = rsa_key_qr.read() key_pem = extract_key_from_qr(qr_image_data) if key_pem: rsa_key_data = key_pem.encode('utf-8') - rsa_key_from_qr = True # QR keys are never password-protected + rsa_key_from_qr = True else: - flash('Could not extract RSA key from QR code image. Make sure the image contains a valid QR code.', 'error') - return render_template('encode.html', day_of_week=day_of_week, has_qrcode_read=HAS_QRCODE_READ) + flash('Could not extract RSA key from QR code image.', 'error') + return render_template('encode.html', has_qrcode_read=HAS_QRCODE_READ) # Validate security factors result = validate_security_factors(pin, rsa_key_data) if not result.is_valid: flash(result.error_message, 'error') - return render_template('encode.html', day_of_week=day_of_week, has_qrcode_read=HAS_QRCODE_READ) + return render_template('encode.html', has_qrcode_read=HAS_QRCODE_READ) # Validate PIN if provided if pin: result = validate_pin(pin) if not result.is_valid: flash(result.error_message, 'error') - return render_template('encode.html', day_of_week=day_of_week, has_qrcode_read=HAS_QRCODE_READ) + return render_template('encode.html', has_qrcode_read=HAS_QRCODE_READ) - # Determine key password - QR code keys are never password-protected + # Determine key password key_password = None if rsa_key_from_qr else (rsa_password if rsa_password else None) # Validate RSA key if provided @@ -614,31 +631,24 @@ def encode_page(): result = validate_rsa_key(rsa_key_data, key_password) if not result.is_valid: flash(result.error_message, 'error') - return render_template('encode.html', day_of_week=day_of_week, has_qrcode_read=HAS_QRCODE_READ) + return render_template('encode.html', has_qrcode_read=HAS_QRCODE_READ) # Validate carrier image result = validate_image(carrier_data, "Carrier image") if not result.is_valid: flash(result.error_message, 'error') - return render_template('encode.html', day_of_week=day_of_week, has_qrcode_read=HAS_QRCODE_READ) + return render_template('encode.html', has_qrcode_read=HAS_QRCODE_READ) - # Get date - 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') - - # Encode with selected mode, output format, and color mode + # v3.2.0: No date parameter needed encode_result = encode( message=payload, reference_photo=ref_data, carrier_image=carrier_data, - day_phrase=day_phrase, + passphrase=passphrase, # v3.2.0: Renamed from day_phrase pin=pin, rsa_key_data=rsa_key_data, rsa_password=key_password, - date_str=date_str, + # date_str removed in v3.2.0 embed_mode=embed_mode, dct_output_format=dct_output_format if embed_mode == 'dct' else None, dct_color_mode=dct_color_mode if embed_mode == 'dct' else None, @@ -648,7 +658,6 @@ def encode_page(): if embed_mode == 'dct' and dct_output_format == 'jpeg': output_ext = '.jpg' output_mime = 'image/jpeg' - # Modify filename extension if needed filename = encode_result.filename if filename.endswith('.png'): filename = filename[:-4] + '.jpg' @@ -674,15 +683,15 @@ def encode_page(): except CapacityError as e: flash(str(e), 'error') - return render_template('encode.html', day_of_week=day_of_week, has_qrcode_read=HAS_QRCODE_READ) + return render_template('encode.html', has_qrcode_read=HAS_QRCODE_READ) except StegasooError as e: flash(str(e), 'error') - return render_template('encode.html', day_of_week=day_of_week, has_qrcode_read=HAS_QRCODE_READ) + return render_template('encode.html', has_qrcode_read=HAS_QRCODE_READ) except Exception as e: flash(f'Error: {e}', 'error') - return render_template('encode.html', day_of_week=day_of_week, has_qrcode_read=HAS_QRCODE_READ) + return render_template('encode.html', has_qrcode_read=HAS_QRCODE_READ) - return render_template('encode.html', day_of_week=day_of_week, has_qrcode_read=HAS_QRCODE_READ) + return render_template('encode.html', has_qrcode_read=HAS_QRCODE_READ) @app.route('/encode/result/') @@ -707,7 +716,7 @@ def encode_result(file_id): thumbnail_url=url_for('encode_thumbnail', thumb_id=thumbnail_id) if thumbnail_id else None, embed_mode=file_info.get('embed_mode', 'lsb'), output_format=file_info.get('output_format', 'png'), - color_mode=file_info.get('color_mode'), # NEW in v3.0.1 + color_mode=file_info.get('color_mode'), ) @@ -787,8 +796,8 @@ def decode_page(): flash('Both reference photo and stego image are required', 'error') return render_template('decode.html', has_qrcode_read=HAS_QRCODE_READ) - # Get form data - day_phrase = request.form.get('day_phrase', '') + # Get form data - v3.2.0: renamed from day_phrase to passphrase + passphrase = request.form.get('passphrase', '') # v3.2.0: Renamed pin = request.form.get('pin', '').strip() rsa_password = request.form.get('rsa_password', '') @@ -802,11 +811,11 @@ def decode_page(): flash('DCT mode requires scipy. Install with: pip install scipy', 'error') return render_template('decode.html', has_qrcode_read=HAS_QRCODE_READ) - # Get encoding date from form (detected from filename in JS) - stego_date = request.form.get('stego_date', '').strip() + # v3.2.0: Removed date handling (no stego_date needed) - if not day_phrase: - flash('Day phrase is required', 'error') + # v3.2.0: Renamed from day_phrase + if not passphrase: + flash('Passphrase is required', 'error') return render_template('decode.html', has_qrcode_read=HAS_QRCODE_READ) # Read files @@ -816,20 +825,18 @@ def decode_page(): # Handle RSA key - can come from .pem file or QR code image rsa_key_data = None rsa_key_qr = request.files.get('rsa_key_qr') - rsa_key_from_qr = False # Track source for password handling + rsa_key_from_qr = False if rsa_key_file and rsa_key_file.filename: - # RSA key from .pem file rsa_key_data = rsa_key_file.read() elif rsa_key_qr and rsa_key_qr.filename and HAS_QRCODE_READ: - # RSA key from QR code image qr_image_data = rsa_key_qr.read() key_pem = extract_key_from_qr(qr_image_data) if key_pem: rsa_key_data = key_pem.encode('utf-8') - rsa_key_from_qr = True # QR keys are never password-protected + rsa_key_from_qr = True else: - flash('Could not extract RSA key from QR code image. Make sure the image contains a valid QR code.', 'error') + flash('Could not extract RSA key from QR code image.', 'error') return render_template('decode.html', has_qrcode_read=HAS_QRCODE_READ) # Validate security factors @@ -845,7 +852,7 @@ def decode_page(): flash(result.error_message, 'error') return render_template('decode.html', has_qrcode_read=HAS_QRCODE_READ) - # Determine key password - QR code keys are never password-protected + # Determine key password key_password = None if rsa_key_from_qr else (rsa_password if rsa_password else None) # Validate RSA key if provided @@ -855,15 +862,15 @@ def decode_page(): flash(result.error_message, 'error') return render_template('decode.html', has_qrcode_read=HAS_QRCODE_READ) - # Decode with selected mode + # v3.2.0: No date_str parameter needed decode_result = decode( stego_image=stego_data, reference_photo=ref_data, - day_phrase=day_phrase, + passphrase=passphrase, # v3.2.0: Renamed from day_phrase pin=pin, rsa_key_data=rsa_key_data, rsa_password=key_password, - date_str=stego_date if stego_date else None, + # date_str removed in v3.2.0 embed_mode=embed_mode, ) @@ -892,7 +899,7 @@ def decode_page(): return render_template('decode.html', decoded_message=decode_result.message) except DecryptionError: - flash('Decryption failed. Check your phrase, PIN, RSA key, and reference photo.', 'error') + flash('Decryption failed. Check your passphrase, PIN, RSA key, and reference photo.', 'error') return render_template('decode.html', has_qrcode_read=HAS_QRCODE_READ) except StegasooError as e: flash(str(e), 'error') diff --git a/frontends/web/templates/about.html b/frontends/web/templates/about.html index 24b163d..ef64701 100644 --- a/frontends/web/templates/about.html +++ b/frontends/web/templates/about.html @@ -27,7 +27,7 @@
  • Multi-Factor Security -
    Combines photo + phrase + PIN/RSA key +
    Combines photo + passphrase + PIN/RSA key
  • @@ -36,8 +36,9 @@
  • - Daily Rotating Phrases -
    Different passphrase each day of the week + Single Passphrase + v3.2.0 +
    Stronger default security
  • @@ -70,7 +71,7 @@ - +
    Embedding Modes
    @@ -213,9 +214,10 @@
    - Daily Phrase -
    Something you know (rotates)
    -
    ~33 bits (3 words)
    + Passphrase + v3.2.0 +
    Something you know
    +
    ~44 bits (4 words)
    @@ -238,7 +240,7 @@
    - Combined entropy: 130-400+ bits depending on configuration. + Combined entropy: 144-424+ bits depending on configuration. For reference, 128 bits is considered computationally infeasible to brute force.
    @@ -348,10 +350,10 @@
    Example: DCT Encode
    # Encode with DCT mode for social media
     curl -X POST "http://localhost:8000/encode/multipart" \
    -  -F "day_phrase=apple forest thunder" \
    +  -F "passphrase=apple forest thunder mountain" \
       -F "pin=123456" \
    -  -F "embedding_mode=dct" \
    -  -F "output_format=jpeg" \
    +  -F "embed_mode=dct" \
    +  -F "dct_output_format=jpeg" \
       -F "reference_photo=@photo.jpg" \
       -F "carrier=@meme.png" \
       -F "message=secret message" \
    @@ -359,17 +361,19 @@ curl -X POST "http://localhost:8000/encode/multipart" \
                     
                     
    Command Line
    # Generate credentials
    -stegasoo generate --pin --words 3
    +stegasoo generate --pin --words 4
     
     # Encode with LSB (default)
    -stegasoo encode -r photo.jpg -c meme.png -p "phrase" --pin 123456 -m "secret"
    +stegasoo encode -r photo.jpg -c meme.png -p "apple forest thunder mountain" \
    +  --pin 123456 -m "secret"
     
     # Encode with DCT for social media
    -stegasoo encode -r photo.jpg -c meme.png -p "phrase" --pin 123456 -m "secret" \
    -  --mode dct --format jpeg
    +stegasoo encode -r photo.jpg -c meme.png -p "apple forest thunder mountain" \
    +  --pin 123456 -m "secret" --mode dct --dct-format jpeg
     
     # Decode (auto-detects mode)
    -stegasoo decode -r photo.jpg -s stego.png -p "phrase" --pin 123456
    +stegasoo decode -r photo.jpg -s stego.png -p "apple forest thunder mountain" \ + --pin 123456

    @@ -403,7 +407,7 @@ stegasoo decode -r photo.jpg -s stego.png -p "phrase" --pin 123456

    1. Both parties agree on a reference photo (shared secretly, never transmitted)
    2. Go to Generate and create credentials
    3. -
    4. Memorize the 7 daily phrases and PIN
    5. +
    6. Memorize the passphrase and PIN
    7. If using RSA, download and securely store the key file
    8. Share credentials with your contact through a secure channel
    @@ -430,7 +434,7 @@ stegasoo decode -r photo.jpg -s stego.png -p "phrase" --pin 123456
  • Upload your reference photo and carrier image
  • Enter your message or select a file to embed
  • -
  • Enter today's phrase and your PIN/key
  • +
  • Enter your passphrase and PIN/key
  • Download the resulting stego image
  • Send through any channel!
  • @@ -451,14 +455,13 @@ stegasoo decode -r photo.jpg -s stego.png -p "phrase" --pin 123456
  • Go to Decode
  • Upload your reference photo (same one used for encoding)
  • Upload the stego image you received
  • -
  • Enter the phrase for the day it was encoded
  • +
  • Enter your passphrase
  • Enter your PIN and/or RSA key
  • View the decoded message or download the extracted file
  • Auto-detection: Stegasoo automatically detects LSB vs DCT mode. - The filename contains the encoding date (e.g., abc123_20251231.png).
    @@ -511,8 +514,10 @@ stegasoo decode -r photo.jpg -s stego.png -p "phrase" --pin 123456 2048, 3072, 4096 bits - Phrase length - 3-12 words (BIP-39) + Passphrase length + v3.2.0 + + 3-12 words (BIP-39, recommended: 4+ words) diff --git a/frontends/web/templates/encode.html b/frontends/web/templates/encode.html index 7fa4770..cfde38f 100644 --- a/frontends/web/templates/encode.html +++ b/frontends/web/templates/encode.html @@ -11,9 +11,9 @@
    - + - +
    +
    -
    @@ -232,19 +239,18 @@
    - +
    - +
    Drop QR image or click to browse
    -
    @@ -373,25 +379,7 @@ {% block scripts %}