A whoooole lotta 4.0.x fixes.

This commit is contained in:
Aaron D. Lee
2026-01-01 22:18:13 -05:00
parent 12929bf326
commit ef7478b30a
40 changed files with 6003 additions and 1830 deletions

View File

@@ -11,11 +11,11 @@
</div>
<div class="card-body">
<p class="lead">
Stegasoo is a secure steganography tool that hides encrypted messages and files
Stegasoo is a steganography tool that hides encrypted messages and files
inside ordinary images using multi-factor authentication.
</p>
<h6 class="text-primary mt-4 mb-3"><i class="bi bi-stars me-2"></i>Key Features</h6>
<h6 class="text-primary mt-4 mb-3">Features</h6>
<div class="row">
<div class="col-md-6">
<ul class="list-unstyled">
@@ -32,39 +32,37 @@
<li class="mb-2">
<i class="bi bi-check-circle text-success me-2"></i>
<strong>AES-256-GCM Encryption</strong>
<br><small class="text-muted">Military-grade authenticated encryption</small>
<br><small class="text-muted">Authenticated encryption with integrity verification</small>
</li>
<li class="mb-2">
<i class="bi bi-check-circle text-success me-2"></i>
<strong>Single Passphrase</strong>
<span class="badge bg-success ms-1">v3.2.0</span>
<br><small class="text-muted">Stronger default security</small>
<strong>LSB &amp; DCT Modes</strong>
<br><small class="text-muted">Choose capacity (LSB) or JPEG resilience (DCT)</small>
</li>
</ul>
</div>
<div class="col-md-6">
<ul class="list-unstyled">
<li class="mb-2">
<i class="bi bi-check-circle text-success me-2"></i>
<strong>DCT Mode</strong>
<span class="badge bg-warning text-dark ms-1">v3.0</span>
<br><small class="text-muted">Survives JPEG recompression for social media</small>
</li>
<li class="mb-2">
<i class="bi bi-check-circle text-success me-2"></i>
<strong>Random Pixel Embedding</strong>
<br><small class="text-muted">Defeats statistical steganalysis</small>
<br><small class="text-muted">Key-derived selection defeats statistical analysis</small>
</li>
<li class="mb-2">
<i class="bi bi-check-circle text-success me-2"></i>
<strong>Large Capacity</strong>
<br><small class="text-muted">Up to {{ max_payload_kb }} KB payload, 24MP images</small>
<strong>Large Image Support</strong>
<br><small class="text-muted">Up to {{ max_payload_kb }} KB payload, tested with 14MB+ images</small>
</li>
<li class="mb-2">
<i class="bi bi-check-circle text-success me-2"></i>
<strong>Zero Server Storage</strong>
<br><small class="text-muted">Nothing saved, files auto-expire</small>
</li>
<li class="mb-2">
<i class="bi bi-check-circle text-success me-2"></i>
<strong>QR Code Keys</strong>
<br><small class="text-muted">Import/export RSA keys via QR codes</small>
</li>
</ul>
</div>
</div>
@@ -78,8 +76,7 @@
</div>
<div class="card-body">
<p>
<span class="badge bg-warning text-dark me-1">New in v3.0</span>
Stegasoo now supports two embedding modes, each optimized for different use cases.
Stegasoo supports two embedding modes, each optimized for different use cases.
</p>
<div class="row mt-4">
@@ -120,7 +117,6 @@
<div class="card-header">
<i class="bi bi-soundwave text-warning me-2"></i>
<strong>DCT Mode</strong>
<span class="badge bg-warning text-dark ms-2">v3.0</span>
</div>
<div class="card-body">
<p class="small">
@@ -200,7 +196,7 @@
<h5 class="mb-0"><i class="bi bi-shield-lock me-2"></i>How Security Works</h5>
</div>
<div class="card-body">
<p>Stegasoo uses <strong>hybrid multi-factor authentication</strong> to derive encryption keys:</p>
<p>Stegasoo uses <strong>multi-factor authentication</strong> to derive encryption keys:</p>
<div class="row text-center my-4">
<div class="col-md-3 mb-3">
@@ -215,7 +211,6 @@
<div class="p-3 bg-dark rounded">
<i class="bi bi-chat-quote text-warning fs-2 d-block mb-2"></i>
<strong>Passphrase</strong>
<span class="badge bg-success ms-1">v3.2.0</span>
<div class="small text-muted mt-1">Something you know</div>
<div class="small text-success">~44 bits (4 words)</div>
</div>
@@ -224,7 +219,7 @@
<div class="p-3 bg-dark rounded">
<i class="bi bi-123 text-danger fs-2 d-block mb-2"></i>
<strong>Static PIN</strong>
<div class="small text-muted mt-1">Something you know (fixed)</div>
<div class="small text-muted mt-1">Something you know</div>
<div class="small text-success">~20 bits (6 digits)</div>
</div>
</div>
@@ -233,7 +228,7 @@
<i class="bi bi-key text-primary fs-2 d-block mb-2"></i>
<strong>RSA Key</strong>
<div class="small text-muted mt-1">Something you have (optional)</div>
<div class="small text-success">~128 bits (2048-bit)</div>
<div class="small text-success">~128 bits</div>
</div>
</div>
</div>
@@ -247,148 +242,77 @@
<h6 class="mt-4">Key Derivation</h6>
<p>
{% if has_argon2 %}
<span class="badge bg-success me-1"><i class="bi bi-check"></i> Argon2id Available</span>
Using <strong>Argon2id</strong> with 256MB memory cost — the winner of the Password Hashing Competition
and current best practice for key derivation. This makes GPU/ASIC attacks infeasible.
<span class="badge bg-success me-1"><i class="bi bi-check"></i> Argon2id</span>
Using <strong>Argon2id</strong> with 256MB memory cost — memory-hard KDF that
makes GPU/ASIC attacks infeasible.
{% else %}
<span class="badge bg-warning text-dark me-1"><i class="bi bi-exclamation-triangle"></i> Argon2 Not Available</span>
Falling back to <strong>PBKDF2-SHA512</strong> with 600,000 iterations.
Install <code>argon2-cffi</code> for stronger security.
{% endif %}
</p>
<h6 class="mt-4">Steganography Techniques</h6>
<p>
<strong>LSB Mode:</strong> Uses Least Significant Bit embedding with pseudo-random pixel selection.
The pixel locations are determined by a key derived from your credentials, making the
hidden data's location unpredictable without the correct inputs.
</p>
<p>
<strong>DCT Mode:</strong> Uses Discrete Cosine Transform embedding with Quantization Index Modulation (QIM).
Data is hidden in mid-frequency coefficients of 8×8 blocks, making it resilient to JPEG recompression.
{% if has_dct %}
<span class="badge bg-success"><i class="bi bi-check"></i> DCT Available</span>
{% else %}
<span class="badge bg-secondary">DCT Requires scipy</span>
{% endif %}
</p>
</div>
</div>
<!-- Version History -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0"><i class="bi bi-file-earmark-binary me-2"></i>File Embedding</h5>
<h5 class="mb-0"><i class="bi bi-clock-history me-2"></i>Version History</h5>
</div>
<div class="card-body">
<p>
Stegasoo supports embedding <strong>any file type</strong>, not just text messages.
</p>
<div class="row">
<div class="col-md-6">
<h6><i class="bi bi-check2-square text-success me-2"></i>Supported</h6>
<ul class="small">
<li>PDF documents</li>
<li>ZIP/RAR archives</li>
<li>Office documents (DOCX, XLSX, PPTX)</li>
<li>Source code files</li>
<li>Any binary file up to {{ max_payload_kb }} KB</li>
</ul>
</div>
<div class="col-md-6">
<h6><i class="bi bi-info-circle text-info me-2"></i>How It Works</h6>
<ul class="small">
<li>Original filename is preserved</li>
<li>MIME type is stored for proper handling</li>
<li>File is encrypted identically to text</li>
<li>Decoding auto-detects text vs. file</li>
</ul>
</div>
<div class="table-responsive">
<table class="table table-dark table-sm small">
<thead>
<tr>
<th>Version</th>
<th>Changes</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>4.0.0</strong></td>
<td>
Simplified auth (no date dependency), passphrase replaces day_phrase,
4-word default, JPEG normalization fix, large image support (14MB+ tested),
subprocess isolation for stability, Python 3.10-3.12 required
</td>
</tr>
<tr>
<td>3.2.0</td>
<td>Single passphrase (removed day-of-week rotation), increased default words</td>
</tr>
<tr>
<td>3.0.0</td>
<td>DCT steganography mode, JPEG output, color preservation option</td>
</tr>
<tr>
<td>2.2.0</td>
<td>QR code RSA key import/export</td>
</tr>
<tr>
<td>2.1.0</td>
<td>File embedding, compression support</td>
</tr>
<tr>
<td>2.0.0</td>
<td>Web UI, REST API, RSA key support</td>
</tr>
<tr>
<td>1.0.0</td>
<td>Initial release, CLI only, LSB mode</td>
</tr>
</tbody>
</table>
</div>
<div class="alert alert-info small mt-3">
<i class="bi bi-lightbulb me-2"></i>
<strong>Tip:</strong> For larger files, compress them first (ZIP) to maximize capacity.
Note that DCT mode has ~10× less capacity than LSB mode.
<div class="alert alert-warning small mt-3 mb-0">
<i class="bi bi-exclamation-triangle me-2"></i>
<strong>Compatibility:</strong> v4.0 cannot decode messages from v3.1 or earlier (different format).
Messages encoded with v3.2 should decode correctly.
</div>
</div>
</div>
<!-- REST API Card -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0"><i class="bi bi-braces me-2"></i>REST API</h5>
</div>
<div class="card-body">
<p>
<span class="badge bg-success me-1"><i class="bi bi-check-circle"></i> FastAPI</span>
Stegasoo includes a complete REST API with automatic documentation and type validation.
</p>
<h6 class="mt-4"><i class="bi bi-layers me-2"></i>Endpoints</h6>
<div class="row">
<div class="col-md-6">
<ul class="small">
<li><code>POST /generate</code> Generate credentials</li>
<li><code>POST /encode</code> Encode text (JSON)</li>
<li><code>POST /encode/multipart</code> Encode with uploads</li>
<li><code>POST /decode</code> Decode message (JSON)</li>
<li><code>POST /decode/multipart</code> Decode with uploads</li>
</ul>
</div>
<div class="col-md-6">
<ul class="small">
<li><code>POST /image/info</code> Get image capacity</li>
<li><code>POST /extract-key-from-qr</code> Extract RSA from QR</li>
<li><code>GET /</code> API status and capabilities</li>
<li><code>GET /docs</code> Swagger documentation</li>
<li><code>GET /redoc</code> ReDoc documentation</li>
</ul>
</div>
</div>
<h6 class="mt-4"><i class="bi bi-code-slash me-2"></i>Example: DCT Encode</h6>
<pre class="bg-dark p-3 rounded small"><code># Encode with DCT mode for social media
curl -X POST "http://localhost:8000/encode/multipart" \
-F "passphrase=apple forest thunder mountain" \
-F "pin=123456" \
-F "embed_mode=dct" \
-F "dct_output_format=jpeg" \
-F "reference_photo=@photo.jpg" \
-F "carrier=@meme.png" \
-F "message=secret message" \
--output stego.jpg</code></pre>
<h6 class="mt-4"><i class="bi bi-terminal me-2"></i>Command Line</h6>
<pre class="bg-dark p-3 rounded small"><code># Generate credentials
stegasoo generate --pin --words 4
# Encode with LSB (default)
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 "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 "apple forest thunder mountain" \
--pin 123456</code></pre>
<p class="small text-muted mt-3 mb-0">
<span class="badge bg-{% if has_argon2 %}success{% else %}warning{% endif %} me-1">
{% if has_argon2 %}Argon2{% else %}PBKDF2{% endif %}
</span>
<span class="badge bg-{% if has_dct %}success{% else %}secondary{% endif %} me-1">
{% if has_dct %}DCT Available{% else %}DCT Unavailable{% endif %}
</span>
<span class="badge bg-{% if has_qrcode_read %}success{% else %}secondary{% endif %}">
{% if has_qrcode_read %}QR Reading{% else %}No QR Reading{% endif %}
</span>
</p>
</div>
</div>
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0"><i class="bi bi-question-circle me-2"></i>Usage Guide</h5>
@@ -470,7 +394,7 @@ stegasoo decode -r photo.jpg -s stego.png -p "apple forest thunder mountain" \
</div>
</div>
<div class="card">
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0"><i class="bi bi-speedometer2 me-2"></i>Limits &amp; Specifications</h5>
</div>
@@ -514,11 +438,13 @@ stegasoo decode -r photo.jpg -s stego.png -p "apple forest thunder mountain" \
<td><strong>2048, 3072, 4096 bits</strong></td>
</tr>
<tr>
<td><i class="bi bi-chat-quote me-2"></i>Passphrase length
<span class="badge bg-success ms-1">v3.2.0</span>
</td>
<td><i class="bi bi-chat-quote me-2"></i>Passphrase length</td>
<td><strong>3-12 words</strong> (BIP-39, recommended: 4+ words)</td>
</tr>
<tr>
<td><i class="bi bi-code me-2"></i>Python version</td>
<td><strong>3.10-3.12</strong> (3.13 not supported)</td>
</tr>
</tbody>
</table>
</div>
@@ -528,7 +454,7 @@ stegasoo decode -r photo.jpg -s stego.png -p "apple forest thunder mountain" \
<p>
Stegasoo v{{ version }} &bull;
<i class="bi bi-github me-1"></i>Open Source &bull;
Built with Python, Flask/FastAPI, and cryptography
Built with Python, Flask, and cryptography
</p>
</div>
</div>

View File

@@ -3,6 +3,53 @@
{% block title %}Decode Message - Stegasoo{% endblock %}
{% block content %}
<style>
/* Glowing passphrase input */
.passphrase-input {
background: rgba(30, 40, 50, 0.8) !important;
border: 2px solid rgba(99, 179, 237, 0.3) !important;
color: #63b3ed !important;
font-family: 'Courier New', monospace;
font-size: 1.1rem;
letter-spacing: 0.5px;
padding: 12px 16px;
transition: border-color 0.3s ease, box-shadow 0.3s ease, background 0.3s ease;
}
.passphrase-input:focus {
border-color: rgba(99, 179, 237, 0.8) !important;
box-shadow: 0 0 20px rgba(99, 179, 237, 0.4), 0 0 40px rgba(99, 179, 237, 0.2) !important;
background: rgba(30, 40, 50, 0.95) !important;
}
.passphrase-input::placeholder {
color: rgba(99, 179, 237, 0.4);
}
/* Glowing PIN input */
.pin-input-container .form-control {
background: rgba(30, 40, 50, 0.8) !important;
border: 2px solid rgba(246, 173, 85, 0.3) !important;
color: #f6ad55 !important;
font-family: 'Courier New', monospace;
font-size: 1.2rem;
letter-spacing: 3px;
text-align: center;
transition: all 0.3s ease;
}
.pin-input-container .form-control:focus {
border-color: rgba(246, 173, 85, 0.8) !important;
box-shadow: 0 0 20px rgba(246, 173, 85, 0.4), 0 0 40px rgba(246, 173, 85, 0.2) !important;
background: rgba(30, 40, 50, 0.95) !important;
}
.pin-input-container .form-control::placeholder {
color: rgba(246, 173, 85, 0.4);
letter-spacing: 1px;
}
</style>
<div class="row justify-content-center">
<div class="col-lg-8">
<div class="card">
@@ -17,14 +64,14 @@
</div>
<label class="form-label text-muted">Decoded Message:</label>
<div class="position-relative">
<div class="alert-message p-3 rounded bg-dark border border-secondary" id="decodedContent" style="white-space: pre-wrap;">{{ decoded_message }}</div>
<button class="btn btn-sm btn-outline-light position-absolute top-0 end-0 m-2" onclick="navigator.clipboard.writeText(document.getElementById('decodedContent').innerText).then(() => this.innerHTML = '<i class=\'bi bi-check\'></i>').catch(() => alert('Failed to copy'))">
<i class="bi bi-clipboard"></i> Copy
</button>
<div class="alert-message p-3 rounded bg-dark border border-secondary mb-2" id="decodedContent" style="white-space: pre-wrap;">{{ decoded_message }}</div>
<div class="d-flex justify-content-end mb-3">
<button class="btn btn-sm btn-outline-light" onclick="navigator.clipboard.writeText(document.getElementById('decodedContent').innerText).then(() => { this.innerHTML = '<i class=\'bi bi-check\'></i> Copied!'; setTimeout(() => this.innerHTML = '<i class=\'bi bi-clipboard\'></i> Copy', 2000); }).catch(() => alert('Failed to copy'))">
<i class="bi bi-clipboard"></i> Copy
</button>
</div>
<a href="/decode" class="btn btn-outline-light w-100 mt-3">
<a href="/decode" class="btn btn-outline-light w-100">
<i class="bi bi-arrow-repeat me-2"></i>Decode Another
</a>
@@ -99,7 +146,7 @@
<label class="form-label">
<i class="bi bi-chat-quote me-1"></i> Passphrase
</label>
<input type="text" name="passphrase" class="form-control"
<input type="text" name="passphrase" id="passphraseInput" class="form-control passphrase-input"
placeholder="e.g., correct horse battery staple" required>
<div class="form-text">
The passphrase used during encoding (typically 4 words)
@@ -114,64 +161,62 @@
</h6>
<div class="row">
<div class="col-md-6 mb-3">
<div class="col-md-4 mb-3">
<label class="form-label"><i class="bi bi-123 me-1"></i> PIN</label>
<div class="input-group">
<input type="password" name="pin" class="form-control" id="pinInput" placeholder="6-9 digits" maxlength="9">
<div class="input-group pin-input-container">
<input type="password" name="pin" class="form-control" id="pinInput" placeholder="••••••" maxlength="9" style="max-width: 180px;">
<button class="btn btn-outline-secondary" type="button" id="togglePin">
<i class="bi bi-eye"></i>
</button>
</div>
<div class="form-text">
If PIN was used during encoding
</div>
<div class="form-text">If PIN was used during encoding</div>
</div>
<div class="col-md-6 mb-3">
<div class="col-md-8 mb-3">
<label class="form-label">
<i class="bi bi-file-earmark-lock me-1"></i> RSA Key
</label>
<ul class="nav nav-tabs nav-tabs-sm mb-2" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active py-1 px-2 small" data-bs-toggle="tab" data-bs-target="#rsaFileTabDec" type="button">
<i class="bi bi-file-earmark me-1"></i>.pem File
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link py-1 px-2 small" data-bs-toggle="tab" data-bs-target="#rsaQrTabDec" type="button">
<i class="bi bi-qr-code me-1"></i>QR Code
</button>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane fade show active" id="rsaFileTabDec" role="tabpanel">
<input type="file" name="rsa_key" class="form-control form-control-sm" id="rsaKeyInput" accept=".pem,.key,application/x-pem-file">
</div>
<div class="tab-pane fade" id="rsaQrTabDec" role="tabpanel">
<input type="file" name="rsa_key_qr" class="form-control form-control-sm" id="rsaKeyQrInput" accept="image/*">
<div class="form-text small">PNG, JPG, or other image of QR code</div>
<!-- RSA Input Method Toggle -->
<div class="btn-group w-100 mb-2" role="group">
<input type="radio" class="btn-check" name="rsa_input_method" id="rsaMethodFile" value="file" checked>
<label class="btn btn-outline-secondary btn-sm" for="rsaMethodFile">
<i class="bi bi-file-earmark me-1"></i>.pem File
</label>
<input type="radio" class="btn-check" name="rsa_input_method" id="rsaMethodQr" value="qr">
<label class="btn btn-outline-secondary btn-sm" for="rsaMethodQr">
<i class="bi bi-qr-code me-1"></i>QR Code
</label>
</div>
<!-- .pem File Input -->
<div id="rsaFileSection">
<input type="file" name="rsa_key" class="form-control form-control-sm" id="rsaKeyInput" accept=".pem,.key,application/x-pem-file">
</div>
<!-- QR Code Input -->
<div id="rsaQrSection" class="d-none">
<div class="drop-zone p-3" id="qrDropZone">
<input type="file" name="rsa_key_qr" accept="image/*" id="rsaKeyQrInput">
<div class="drop-zone-label text-center">
<i class="bi bi-qr-code-scan fs-4 d-block text-muted mb-1"></i>
<span class="text-muted small">Drop QR image or click to browse</span>
</div>
<img class="drop-zone-preview d-none" id="qrPreview" style="max-height: 80px;">
</div>
</div>
<div class="form-text">
If RSA key was used during encoding (file or QR image)
<!-- Key Password (always visible) -->
<div class="input-group input-group-sm mt-2">
<input type="password" name="rsa_password" class="form-control" id="rsaPasswordInput" placeholder="Key password (if encrypted)">
<button class="btn btn-outline-secondary" type="button" id="toggleRsaPassword">
<i class="bi bi-eye"></i>
</button>
</div>
</div>
</div>
<!-- RSA Key Password (shown when key selected) -->
<div class="mb-3 d-none" id="rsaPasswordGroup">
<label class="form-label">
<i class="bi bi-key me-1"></i> RSA Key Password
</label>
<input type="password" name="rsa_password" class="form-control"
placeholder="Password for the .pem file (if encrypted)">
<div class="form-text">
Leave blank if your key file is not password-protected
</div>
</div>
<!-- ================================================================
ADVANCED OPTIONS (v3.0) - Extraction Mode
================================================================ -->
@@ -276,7 +321,7 @@
</li>
<li class="mb-2">
<i class="bi bi-exclamation-triangle-fill text-warning me-1"></i>
<strong>Format compatibility:</strong> v3.2.0 cannot decode messages from v3.1.0 (different format)
<strong>Format compatibility:</strong> v4.0 cannot decode messages from v3.1 or earlier (different format)
</li>
<li class="mb-2">
<i class="bi bi-info-circle-fill text-info me-1"></i>
@@ -304,22 +349,6 @@ document.getElementById('decodeForm')?.addEventListener('submit', function() {
btn.disabled = true;
});
// Show RSA password field when key is selected
const rsaKeyInput = document.getElementById('rsaKeyInput');
const rsaKeyQrInput = document.getElementById('rsaKeyQrInput');
const rsaPasswordGroup = document.getElementById('rsaPasswordGroup');
function checkRsaKeySelected() {
const hasFile = (rsaKeyInput && rsaKeyInput.files.length > 0) ||
(rsaKeyQrInput && rsaKeyQrInput.files.length > 0);
if (rsaPasswordGroup) {
rsaPasswordGroup.classList.toggle('d-none', !hasFile);
}
}
rsaKeyInput?.addEventListener('change', checkRsaKeySelected);
rsaKeyQrInput?.addEventListener('change', checkRsaKeySelected);
// PIN Toggle
document.getElementById('togglePin')?.addEventListener('click', function() {
const input = document.getElementById('pinInput');
@@ -333,6 +362,35 @@ document.getElementById('togglePin')?.addEventListener('click', function() {
}
});
// RSA Password Toggle
document.getElementById('toggleRsaPassword')?.addEventListener('click', function() {
const input = document.getElementById('rsaPasswordInput');
const icon = this.querySelector('i');
if (input.type === 'password') {
input.type = 'text';
icon.classList.replace('bi-eye', 'bi-eye-slash');
} else {
input.type = 'password';
icon.classList.replace('bi-eye-slash', 'bi-eye');
}
});
// RSA Input Method Toggle (File vs QR)
const rsaMethodFile = document.getElementById('rsaMethodFile');
const rsaMethodQr = document.getElementById('rsaMethodQr');
const rsaFileSection = document.getElementById('rsaFileSection');
const rsaQrSection = document.getElementById('rsaQrSection');
function updateRsaInputMethod() {
if (!rsaMethodFile || !rsaFileSection || !rsaQrSection) return;
const isFile = rsaMethodFile.checked;
rsaFileSection.classList.toggle('d-none', !isFile);
rsaQrSection.classList.toggle('d-none', isFile);
}
rsaMethodFile?.addEventListener('change', updateRsaInputMethod);
rsaMethodQr?.addEventListener('change', updateRsaInputMethod);
// Mode card highlighting
const autoModeCard = document.getElementById('autoModeCard');
const lsbModeCardDec = document.getElementById('lsbModeCardDec');
@@ -428,12 +486,85 @@ document.querySelectorAll('.drop-zone').forEach(zone => {
const reader = new FileReader();
reader.onload = e => {
preview.src = e.target.result;
preview.classList.remove('d-none');
if (preview) {
preview.src = e.target.result;
preview.classList.remove('d-none');
}
label.innerHTML = '<i class="bi bi-check-circle text-success me-1"></i>' + file.name;
};
reader.readAsDataURL(file);
}
});
// QR Code RSA Key scanning
const rsaKeyQrInput = document.getElementById('rsaKeyQrInput');
const qrPreview = document.getElementById('qrPreview');
if (rsaKeyQrInput) {
rsaKeyQrInput.addEventListener('change', function() {
if (this.files && this.files[0]) {
const file = this.files[0];
// Show image preview
if (file.type.startsWith('image/')) {
const reader = new FileReader();
reader.onload = e => {
if (qrPreview) {
qrPreview.src = e.target.result;
qrPreview.classList.remove('d-none');
}
};
reader.readAsDataURL(file);
}
// Extract key from QR
const formData = new FormData();
formData.append('qr_image', file);
fetch('/extract-key-from-qr', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (!data.success) {
alert('QR decode failed: ' + data.error);
return;
}
// Visual feedback
document.querySelector('#qrDropZone .drop-zone-label').innerHTML =
'<i class="bi bi-check-circle text-success me-1"></i>RSA Key loaded from QR';
})
.catch(err => {
alert('QR decode failed: ' + err);
});
}
});
}
// Auto-resize passphrase input font to fit long passphrases
const passphraseInput = document.getElementById('passphraseInput');
if (passphraseInput) {
// Stepped font sizes (characters -> rem)
const fontSizeSteps = [
{ maxChars: 30, size: 1.1 },
{ maxChars: 45, size: 1.0 },
{ maxChars: 60, size: 0.95 },
{ maxChars: Infinity, size: 0.9 }
];
function adjustPassphraseFontSize() {
const len = passphraseInput.value.length;
for (const step of fontSizeSteps) {
if (len <= step.maxChars) {
passphraseInput.style.fontSize = step.size + 'rem';
break;
}
}
}
passphraseInput.addEventListener('input', adjustPassphraseFontSize);
adjustPassphraseFontSize(); // Initial call in case of pre-filled value
}
</script>
{% endblock %}

View File

@@ -3,6 +3,57 @@
{% block title %}Encode Message - Stegasoo{% endblock %}
{% block content %}
<style>
/* Glowing passphrase input */
.passphrase-input-container {
position: relative;
}
.passphrase-input {
background: rgba(30, 40, 50, 0.8) !important;
border: 2px solid rgba(99, 179, 237, 0.3) !important;
color: #63b3ed !important;
font-family: 'Courier New', monospace;
font-size: 1.1rem;
letter-spacing: 0.5px;
padding: 12px 16px;
transition: border-color 0.3s ease, box-shadow 0.3s ease, background 0.3s ease;
}
.passphrase-input:focus {
border-color: rgba(99, 179, 237, 0.8) !important;
box-shadow: 0 0 20px rgba(99, 179, 237, 0.4), 0 0 40px rgba(99, 179, 237, 0.2) !important;
background: rgba(30, 40, 50, 0.95) !important;
}
.passphrase-input::placeholder {
color: rgba(99, 179, 237, 0.4);
}
/* Glowing PIN input */
.pin-input-container .form-control {
background: rgba(30, 40, 50, 0.8) !important;
border: 2px solid rgba(246, 173, 85, 0.3) !important;
color: #f6ad55 !important;
font-family: 'Courier New', monospace;
font-size: 1.2rem;
letter-spacing: 3px;
text-align: center;
transition: all 0.3s ease;
}
.pin-input-container .form-control:focus {
border-color: rgba(246, 173, 85, 0.8) !important;
box-shadow: 0 0 20px rgba(246, 173, 85, 0.4), 0 0 40px rgba(246, 173, 85, 0.2) !important;
background: rgba(30, 40, 50, 0.95) !important;
}
.pin-input-container .form-control::placeholder {
color: rgba(246, 173, 85, 0.4);
letter-spacing: 1px;
}
</style>
<div class="row justify-content-center">
<div class="col-lg-8">
<div class="card">
@@ -11,7 +62,7 @@
</div>
<div class="card-body">
<form method="POST" enctype="multipart/form-data" id="encodeForm">
<!-- v3.2.0: Removed client_date hidden field -->
<!-- Removed client_date hidden field -->
<!-- Embedding Mode Selection -->
<div class="mb-4">
@@ -182,15 +233,16 @@
</div>
</div>
<!-- v3.2.0: Renamed from day_phrase to passphrase, removed date selection -->
<!-- Passphrase input with glow styling -->
<div class="mb-3">
<label class="form-label" id="passphraseLabel">
<i class="bi bi-chat-quote me-1"></i> Passphrase
<span class="badge bg-success ms-2">v3.2.0</span>
</label>
<input type="text" name="passphrase" class="form-control"
placeholder="e.g., apple forest thunder mountain" required
id="passphraseInput">
<div class="passphrase-input-container">
<input type="text" name="passphrase" class="form-control passphrase-input"
placeholder="e.g., apple forest thunder mountain" required
id="passphraseInput">
</div>
<div class="form-text">
Your passphrase for this message
</div>
@@ -210,8 +262,8 @@
<div class="row">
<div class="col-md-4 mb-3">
<label class="form-label"><i class="bi bi-123 me-1"></i> PIN</label>
<div class="input-group">
<input type="password" name="pin" class="form-control" id="pinInput" placeholder="6-9 digits" maxlength="9" style="max-width: 140px;">
<div class="input-group pin-input-container">
<input type="password" name="pin" class="form-control" id="pinInput" placeholder="••••••" maxlength="9" style="max-width: 180px;">
<button class="btn btn-outline-secondary" type="button" id="togglePin">
<i class="bi bi-eye"></i>
</button>
@@ -250,6 +302,7 @@
<i class="bi bi-qr-code-scan fs-4 d-block text-muted mb-1"></i>
<span class="text-muted small">Drop QR image or click to browse</span>
</div>
<img class="drop-zone-preview d-none" id="qrPreview" style="max-height: 80px;">
</div>
</div>
@@ -406,12 +459,33 @@ function updatePayloadSection() {
payloadTextRadio.addEventListener('change', updatePayloadSection);
payloadFileRadio.addEventListener('change', updatePayloadSection);
// Passphrase validation (v3.2.0)
// Passphrase validation and auto-resize font
const passphraseInput = document.getElementById('passphraseInput');
const passphraseWarning = document.getElementById('passphraseWarning');
// Stepped font sizes (characters -> rem)
const fontSizeSteps = [
{ maxChars: 30, size: 1.1 },
{ maxChars: 45, size: 1.0 },
{ maxChars: 60, size: 0.95 },
{ maxChars: Infinity, size: 0.9 }
];
function adjustPassphraseFontSize() {
if (!passphraseInput) return;
const len = passphraseInput.value.length;
for (const step of fontSizeSteps) {
if (len <= step.maxChars) {
passphraseInput.style.fontSize = step.size + 'rem';
break;
}
}
}
if (passphraseInput) {
passphraseInput.addEventListener('input', function() {
// Word count warning
const words = this.value.trim().split(/\s+/).filter(w => w.length > 0);
const recommendedWords = {{ recommended_passphrase_words }};
@@ -420,7 +494,13 @@ if (passphraseInput) {
} else {
passphraseWarning.style.display = 'none';
}
// Auto-resize font
adjustPassphraseFontSize();
});
// Initial font size adjustment
adjustPassphraseFontSize();
}
// Payload file info display
@@ -728,11 +808,27 @@ document.addEventListener('paste', function(e) {
// QR Code RSA Key scanning
const rsaQrInput = document.getElementById('rsaQrInput');
const qrPreview = document.getElementById('qrPreview');
if (rsaQrInput) {
rsaQrInput.addEventListener('change', function() {
if (this.files && this.files[0]) {
const file = this.files[0];
// Show image preview
if (file.type.startsWith('image/')) {
const reader = new FileReader();
reader.onload = e => {
if (qrPreview) {
qrPreview.src = e.target.result;
qrPreview.classList.remove('d-none');
}
};
reader.readAsDataURL(file);
}
// Extract key from QR
const formData = new FormData();
formData.append('qr_image', this.files[0]);
formData.append('qr_image', file);
fetch('/extract-key-from-qr', {
method: 'POST',

View File

@@ -120,7 +120,7 @@
{% if embed_mode == 'dct' %}
<li>Recipient needs <strong>DCT mode</strong> or <strong>Auto</strong> detection to decode</li>
{% if color_mode == 'color' %}
<li><span class="badge bg-success">v3.0.1</span> Color preserved - extraction works on both color and grayscale</li>
<li>Color preserved - extraction works on both color and grayscale</li>
{% endif %}
{% endif %}
</ul>

View File

@@ -112,7 +112,6 @@
<div class="mb-4">
<h6 class="text-muted">
<i class="bi bi-chat-quote me-2"></i>PASSPHRASE
<span class="badge bg-success ms-2">v3.2.0</span>
</h6>
<div class="passphrase-container">

View File

@@ -11,7 +11,7 @@
<div style="margin-bottom: 40px;">
<h1 class="display-4 fw-bold mb-2">
Stegasoo
<span class="badge bg-success fs-6 ms-2">v3.2.0</span>
<span class="badge bg-success fs-6 ms-2">v4.0</span>
</h1>
<p class="lead text-muted mb-0">Hide encrypted data in plain sight.</p>
</div>
@@ -94,7 +94,6 @@
<div class="p-3 bg-dark rounded h-100">
<i class="bi bi-soundwave text-warning fs-2 d-block mb-2"></i>
<strong>DCT Mode</strong>
<span class="badge bg-warning text-dark ms-1">v3.0</span>
<div class="small text-muted mt-2">
Survives JPEG recompression<br>
Best for social media
@@ -108,7 +107,7 @@
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0"><i class="bi bi-diagram-3 me-2"></i>How It Works</h5>
<a href="/about" class="btn btn-sm btn-outline-secondary">Learn More</a>
<a href="/about" class="btn btn-sm btn-outline-light">Learn More</a>
</div>
<div class="card-body">
<div class="row">
@@ -117,21 +116,20 @@
<ul class="list-unstyled small">
<li class="mb-1">
<i class="bi bi-image text-info me-2"></i>
<strong>Reference Photo</strong> shared secret image
<strong>Reference Photo</strong> shared secret image
</li>
<li class="mb-1">
<i class="bi bi-chat-quote text-info me-2"></i>
<strong>Passphrase</strong> 4+ words
<span class="badge bg-success ms-1">v3.2.0</span>
<strong>Passphrase</strong> 4+ words
</li>
<li class="mb-1">
<i class="bi bi-123 text-info me-2"></i>
<strong>PIN</strong> 6-9 digits (and/or RSA key)
<strong>PIN</strong> 6-9 digits (and/or RSA key)
</li>
</ul>
</div>
<div class="col-md-6">
<h6 class="text-primary"><i class="bi bi-shield-check me-2"></i>We Provide</h6>
<h6 class="text-primary"><i class="bi bi-shield-check me-2"></i>Security</h6>
<ul class="list-unstyled small">
<li class="mb-1">
<i class="bi bi-lock text-success me-2"></i>