Fix decode progress - add Argon2 phase tracking

- decode.py now writes 20% "initializing" before Argon2
- decode.py writes 25% "extracting" after Argon2 completes
- DCT extraction scales from 25-70% (was 5-70%)
- Removed duplicate "loading" writes that caused backwards jumps

Progress flow: 15% reading -> 20% initializing (Argon2) -> 25-70% extracting -> 75-95% decoding -> 100% complete

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Aaron D. Lee
2026-01-09 23:14:10 -05:00
parent 167e1a6ff5
commit 2ed108f3a0
2 changed files with 29 additions and 5 deletions

View File

@@ -1198,7 +1198,7 @@ def _extract_scipy_dct_safe(
progress_file: str | None = None,
) -> bytes:
"""Extract using safe DCT operations with vectorized processing."""
_write_progress(progress_file, 0, 100, "loading")
# Progress starts at 25% (decode.py writes 20% for Argon2, 25% before extraction)
img = Image.open(io.BytesIO(stego_image))
width, height = img.size
@@ -1264,9 +1264,10 @@ def _extract_scipy_dct_safe(
del blocks, dct_blocks, coeffs, quantized
block_idx = batch_end
# Report progress (scale to 5-70% range, RS decode gets 70-100%)
# Report progress (scale to 25-70% range, RS decode gets 70-100%)
# Starts at 25% because decode.py writes 25% before calling extraction
if progress_file and block_idx % PROGRESS_INTERVAL < BATCH_SIZE:
extract_pct = 5 + int(65 * block_idx / num_blocks)
extract_pct = 25 + int(45 * block_idx / num_blocks)
_write_progress(progress_file, extract_pct, 100, "extracting")
# Check if we have enough bits (early exit)
@@ -1382,7 +1383,7 @@ def _extract_jpegio(
"""Extract using jpegio for JPEG images."""
import os
_write_progress(progress_file, 0, 100, "loading")
# Progress starts at 25% (decode.py writes 20% for Argon2, 25% before extraction)
# Normalize JPEG to avoid crashes with quality=100 images
# (shouldn't happen with stego images, but be defensive)
@@ -1397,7 +1398,7 @@ def _extract_jpegio(
all_positions = _jpegio_get_usable_positions(coef_array)
order = _jpegio_generate_order(len(all_positions), seed)
_write_progress(progress_file, 20, 100, "extracting")
_write_progress(progress_file, 30, 100, "extracting")
# Try RS-protected format first (has 24-byte length prefix: 3 copies for majority voting)
if HAS_REEDSOLO and len(all_positions) >= RS_LENGTH_PREFIX_SIZE * 8:

View File

@@ -8,6 +8,7 @@ Changes in v4.0.0:
- Improved error messages for channel key mismatches
"""
import json
from pathlib import Path
from .constants import EMBED_MODE_AUTO
@@ -24,6 +25,22 @@ from .validation import (
)
def _write_progress(progress_file: str | None, current: int, total: int, phase: str) -> None:
"""Write progress to file for UI polling."""
if progress_file is None:
return
try:
with open(progress_file, "w") as f:
json.dump({
"current": current,
"total": total,
"percent": (current / total * 100) if total > 0 else 0,
"phase": phase,
}, f)
except OSError:
pass
def decode(
stego_image: bytes,
reference_photo: bytes,
@@ -93,11 +110,17 @@ def decode(
if rsa_key_data:
require_valid_rsa_key(rsa_key_data, rsa_password)
# Progress: starting key derivation (Argon2 - slow on Pi)
_write_progress(progress_file, 20, 100, "initializing")
# Derive pixel/coefficient selection key (with channel key)
from .crypto import derive_pixel_key
pixel_key = derive_pixel_key(reference_photo, passphrase, pin, rsa_key_data, channel_key)
# Progress: key derivation done, starting extraction
_write_progress(progress_file, 25, 100, "extracting")
# Extract encrypted data
encrypted = extract_from_image(
stego_image,