From cc812a64e9972d6b951738b5fc958908710aad6d Mon Sep 17 00:00:00 2001
From: brainx <12695242+brainx@users.noreply.github.com>
Date: Tue, 22 Sep 2026 19:45:32 +0200
Subject: [PATCH] feat: enforce expected recipient fingerprints across
encryption workflows
---
README.md | 19 ++-
api_app.py | 46 +++++-
api_jobs.py | 56 ++++++-
crypto_core.py | 29 ++++
docs/API.md | 17 ++-
docs/SCREENSHOTS.md | 22 ++-
.../custom-web-encrypt-workflow.png | Bin 94974 -> 163254 bytes
.../custom-web-large-file-result.png | Bin 0 -> 176364 bytes
.../screenshots/custom-web-mobile-inspect.png | Bin 49048 -> 78857 bytes
pqc_agent_tools.py | 31 ++++
scripts/ui_native_smoke.mjs | 138 ++++++++++++++++--
scripts/ui_smoke.mjs | 2 +-
tests/test_agent_tools.py | 86 ++++++++++-
tests/test_api_app.py | 69 ++++++++-
tests/test_api_jobs_api.py | 101 ++++++++++++-
tests/test_api_jobs_crypto.py | 54 +++++++
tests/test_crypto_core.py | 42 ++++++
web/src/api/client.test.ts | 104 +++++++++++++
web/src/api/client.ts | 30 +++-
web/src/api/contracts.ts | 4 +-
web/src/api/largeFiles.test.ts | 44 +++++-
web/src/api/largeFiles.ts | 13 +-
.../components/RecipientFingerprintField.tsx | 29 ++++
.../encrypt/BatchEncryptWorkflow.test.tsx | 42 ++++++
.../features/encrypt/BatchEncryptWorkflow.tsx | 23 +--
.../features/encrypt/EncryptWorkflow.test.tsx | 70 ++++++++-
web/src/features/encrypt/EncryptWorkflow.tsx | 33 +++--
.../features/encrypt/useBatchEncryption.ts | 6 +-
.../large-files/LargeFilesWorkflow.test.tsx | 56 ++++++-
.../large-files/LargeFilesWorkflow.tsx | 24 +--
web/src/hooks/useLargeFileJob.ts | 6 +-
web/src/lib/recipientFingerprint.ts | 15 ++
web/src/styles/components.css | 15 +-
web/src/test/fixtures.ts | 1 +
34 files changed, 1133 insertions(+), 94 deletions(-)
create mode 100644 docs/screenshots/custom-web-large-file-result.png
create mode 100644 web/src/api/client.test.ts
create mode 100644 web/src/components/RecipientFingerprintField.tsx
create mode 100644 web/src/lib/recipientFingerprint.ts
diff --git a/README.md b/README.md
index c788828..08c715d 100644
--- a/README.md
+++ b/README.md
@@ -10,14 +10,14 @@ A post-quantum cryptography tool for file encryption. New files combine ML-KEM-7
- Monochrome local web interface for ML-KEM-768 + X25519 key generation, file encryption, decryption, and PEM key inspection.
+ Local file encryption with recipient fingerprint checks, batch processing, and large-file jobs.
## Features
@@ -26,6 +26,7 @@ A post-quantum cryptography tool for file encryption. New files combine ML-KEM-7
- **Authenticated File Encryption**: Derives AES-256-GCM keys from both ML-KEM and X25519 shared secrets
- **Password-Protected Keys**: Private keys are always encrypted with scrypt-derived AES-256-GCM keys
- **Public-Key Fingerprints**: Full versioned SHA3-256 identifiers support independent public-key comparison
+- **Expected Recipient Checks**: Optionally require an independently obtained fingerprint before single-file, batch, large-file, or CLI encryption; the backend rejects a different public key
- **User-Friendly Interface**: Custom local web UI with progressive technical details and a Python ASGI API
- **Batch Encryption**: Encrypt up to 25 files for one recipient with sequential processing, per-file results, cancellation, and explicit downloads
- **Batch Decryption**: Restore up to 25 encrypted files with one private key and password, retaining successful results when another file fails
@@ -38,7 +39,7 @@ A post-quantum cryptography tool for file encryption. New files combine ML-KEM-7
## Screenshots
-The current browser smoke captures show the responsive Encrypt and Inspect key workflows. Click either image to open the full screenshot page.
+These captures show the real local app with sample files and freshly generated keys: recipient fingerprint comparison, a completed large-file job, and mobile key inspection. Open the gallery for full-size images.
@@ -49,6 +50,8 @@ The current browser smoke captures show the responsive Encrypt and Inspect key w
+
+
See [docs/SCREENSHOTS.md](docs/SCREENSHOTS.md) for the dedicated screenshot page.
## Project Documentation
@@ -152,6 +155,10 @@ The web app keeps its generated-result references in the current tab's in-memory
Successful key generation and validated public-key inspection return a complete fingerprint in the form `QE1-SHA3-256:<64 lowercase hexadecimal characters>`. The Generate workflow shows the fingerprint for the new pair, Inspect key shows it for a validated public key, and Encrypt shows the recipient fingerprint before encryption. Compare the entire value with the key owner over an independently authenticated channel, separate from the channel that delivered the key.
+To enforce that comparison, paste the independently obtained value into **Expected recipient fingerprint (optional)** in Encrypt, Batch encrypt, or Large files. A malformed or different value blocks submission, and the backend checks it again against the actual key before encryption. Changing the selected public key keeps your expectation so a substitution cannot silently clear the check. Leaving the field empty preserves ordinary encryption. This feature requires an engine advertising recipient-fingerprint support; a supplied expectation cannot be used with an older engine.
+
+For the CLI, add `--expected-recipient-fingerprint "$RECIPIENT_FINGERPRINT"` to `encrypt`, where `RECIPIENT_FINGERPRINT` contains the independently obtained complete value. A malformed or mismatching expectation fails before creating an output or replacing an existing destination.
+
A matching fingerprint identifies the same validated algorithm label and canonical public-key bytes. It does not prove the owner's identity or control of the private key, certify that the key is trustworthy, or protect a comparison performed through the same compromised channel. Fingerprints are public identifiers and do not change the PEM or encrypted-file formats. Metadata-only inspection of an encrypted private key omits the fingerprint because deriving it requires an authenticated password unlock.
## Verification
@@ -184,7 +191,7 @@ When a native `liboqs` installation is available to the running app, also run th
npm run ui-native
```
-Do not treat the browser smoke test as proof that the native cryptographic backend is installed; it verifies the built interface against the local API contract. `npm run ui-native` verifies key generation, encryption, and decryption through the available native backend.
+Do not treat the browser smoke test as proof that the native cryptographic backend is installed; it verifies the built interface against the local API contract. `npm run ui-native` verifies key generation, recipient checks, encryption/decryption, key recovery/password changes, file verification, and large-file jobs through the available native backend. To refresh the committed screenshots during that same run, use `npm run ui-native -- --screenshots`. See [the screenshot capture notes](docs/SCREENSHOTS.md#refreshing-the-images).
### Key Generation
@@ -198,7 +205,7 @@ Do not treat the browser smoke test as proof that the native cryptographic backe
1. Select "Encrypt" from the workflow navigation
2. Upload the file you want to encrypt
3. Upload the recipient's public key (.pem file)
-4. Compare the complete recipient fingerprint over an independently authenticated channel
+4. Obtain the complete fingerprint over an independently authenticated channel and optionally paste it into **Expected recipient fingerprint** to enforce a match
5. Specify the output filename
6. Download the encrypted file
@@ -213,7 +220,7 @@ Do not treat the browser smoke test as proof that the native cryptographic backe
### Batch Encryption
1. Choose **Batch encrypt** and select or drop up to 25 files. Their combined plaintext size must fit the displayed file limit (100 MiB by default).
-2. Select the recipient's public key and compare its complete fingerprint over an independently authenticated channel.
+2. Select the recipient's public key. Optionally enter its independently obtained complete fingerprint to require a match for every file in the batch.
3. Choose **Encrypt batch**. Files run one at a time, with separate progress and error states. A failed file does not discard successful results or automatically retry the failed request.
4. Download each completed result. Batch output names retain the original extension, such as `report.pdf.pqc`; duplicate names receive a numeric suffix.
5. Choose **Clear batch** when finished. Results live only in the current tab; there is no persistent batch history or server-side recovery.
diff --git a/api_app.py b/api_app.py
index 92b0864..3f9f20c 100644
--- a/api_app.py
+++ b/api_app.py
@@ -18,7 +18,7 @@
from starlette.applications import Starlette
from starlette.concurrency import run_in_threadpool
-from starlette.datastructures import UploadFile
+from starlette.datastructures import FormData, UploadFile
from starlette.requests import Request
from starlette.responses import JSONResponse, PlainTextResponse, Response, StreamingResponse
from starlette.routing import BaseRoute, Mount, Route
@@ -533,6 +533,15 @@ def _form_text(form: Any, name: str, required: bool = True) -> str:
return str(value)
+def _form_recipient_fingerprint(form: FormData) -> str | None:
+ values = form.getlist("expected_recipient_fingerprint")
+ if not values:
+ return None
+ if len(values) != 1 or not isinstance(values[0], str):
+ raise core.InvalidRecipientFingerprintError("Provide the complete canonical recipient fingerprint.")
+ return core.validate_recipient_fingerprint(values[0])
+
+
def _form_upload(form: Any, name: str) -> UploadFile:
value = form.get(name)
if not isinstance(value, UploadFile):
@@ -617,6 +626,7 @@ def _health_payload() -> dict[str, Any]:
"supportsKeyPasswordChange": True,
"supportsPublicKeyRecovery": True,
"supportsFileVerification": True,
+ "supportsRecipientFingerprint": True,
"backendReady": current_backend_ready,
"backendMessage": backend_message,
"capabilities": capabilities,
@@ -968,7 +978,7 @@ async def verify_file(request: Request) -> JSONResponse:
await request.close()
-def _encrypt_bytes(input_data: bytes, public_pem: str) -> bytes:
+def _encrypt_bytes(input_data: bytes, public_pem: str, expected_recipient_fingerprint: str | None = None) -> bytes:
public_key_bytes, kem_alg_from_key, key_type = core.load_key_pem(public_pem)
if not public_key_bytes or not kem_alg_from_key or key_type != "public":
raise ApiError(400, "invalid_public_key", "Upload a supported PQC public key PEM file.")
@@ -979,6 +989,8 @@ def _encrypt_bytes(input_data: bytes, public_pem: str) -> bytes:
"Generate a new ML-KEM-768+X25519-v2 public key for encryption.",
)
+ if expected_recipient_fingerprint is not None:
+ core.verify_recipient_fingerprint(public_key_bytes, kem_alg_from_key, expected_recipient_fingerprint)
encrypted_blob = core.encrypt_file_pro(input_data, public_key_bytes, kem_alg_from_key)
del input_data
del public_key_bytes
@@ -990,7 +1002,8 @@ def _encrypt_bytes(input_data: bytes, public_pem: str) -> bytes:
async def encrypt_file(request: Request) -> Response:
try:
- form = await _form(request, max_files=2)
+ form = await _form(request, max_files=3)
+ expected_recipient_fingerprint = _form_recipient_fingerprint(form)
uploaded_file = _form_upload(form, "file")
public_key_file = _form_upload(form, "public_key")
original_filename = Path(uploaded_file.filename or "file")
@@ -1001,12 +1014,26 @@ async def encrypt_file(request: Request) -> Response:
input_data = await _read_upload_bytes(uploaded_file, cfg.MAX_FILE_BYTES, "Input file")
public_pem = await _read_upload_text(public_key_file, cfg.MAX_PEM_BYTES, "Public key file")
- encrypted_blob = await request.state.crypto_lease.run(_encrypt_bytes, input_data, public_pem)
+ encrypted_blob = await request.state.crypto_lease.run(
+ _encrypt_bytes, input_data, public_pem, expected_recipient_fingerprint
+ )
del input_data
return _download_response(encrypted_blob, output_filename)
except ApiError as exc:
return _json_error(exc)
+ except core.InvalidRecipientFingerprintError:
+ return _json_error(
+ ApiError(400, "invalid_recipient_fingerprint", "Provide the complete canonical recipient fingerprint.")
+ )
+ except core.RecipientFingerprintMismatchError:
+ return _json_error(
+ ApiError(
+ 400,
+ "recipient_fingerprint_mismatch",
+ "The public key does not match the expected recipient fingerprint.",
+ )
+ )
except core.CryptoDependencyError:
return _json_error(ApiError(503, "backend_unavailable", "Post-quantum backend is not ready."))
except Exception as exc:
@@ -1080,6 +1107,8 @@ def _job_error(exc: Exception) -> JSONResponse:
error = ApiError(exc.status, exc.code, exc.message)
elif isinstance(exc, ApiError):
error = exc
+ elif isinstance(exc, core.InvalidRecipientFingerprintError):
+ error = ApiError(400, "invalid_recipient_fingerprint", "Provide the complete canonical recipient fingerprint.")
elif isinstance(exc, RequestBodyTooLarge):
error = ApiError(413, "request_too_large", "Request body exceeds the configured size limit.")
elif isinstance(exc, OSError):
@@ -1136,11 +1165,16 @@ async def upload_job(request: Request) -> JSONResponse:
async def start_job(request: Request) -> JSONResponse:
try:
jobs, job = _request_job(request)
- form = await _form(request, max_files=1, max_fields=1)
+ form = await _form(request, max_files=2, max_fields=3)
+ expected_recipient_fingerprint = _form_recipient_fingerprint(form)
+ if expected_recipient_fingerprint is not None and job.mode != "encrypt":
+ raise core.InvalidRecipientFingerprintError("Recipient verification applies only to encryption.")
pem = await _read_upload_text(_form_upload(form, "key"), cfg.MAX_PEM_BYTES, "Key file")
password = "" if job.mode == "encrypt" else _workflow_password(form)
filename = f"{job.filename}.pqc" if job.mode == "encrypt" else guess_decrypted_filename(Path(job.filename))
- jobs.start(job, pem, password, sanitize_download_filename(filename, "download.bin"))
+ jobs.start(
+ job, pem, password, sanitize_download_filename(filename, "download.bin"), expected_recipient_fingerprint
+ )
return _success_json({"job": job.snapshot()})
except Exception as exc:
return _job_error(exc)
diff --git a/api_jobs.py b/api_jobs.py
index 6824b6b..71f3d32 100644
--- a/api_jobs.py
+++ b/api_jobs.py
@@ -236,15 +236,35 @@ async def upload(self, job: FileJob, chunks: Any) -> None:
finally:
job.upload_task = None
- def start(self, job: FileJob, pem: str, password: str, output_filename: str) -> None:
+ def start(
+ self,
+ job: FileJob,
+ pem: str,
+ password: str,
+ output_filename: str,
+ expected_recipient_fingerprint: str | None = None,
+ ) -> None:
if job.state != "ready":
raise JobError(409, "invalid_job_state", "Upload a complete file before starting this job.")
+ if expected_recipient_fingerprint is not None:
+ core.validate_recipient_fingerprint(expected_recipient_fingerprint)
+ if job.mode != "encrypt":
+ raise core.InvalidRecipientFingerprintError("Recipient verification applies only to encryption.")
job.state = "running"
job.phase = "preparing"
job.processed = 0
- job.task = asyncio.create_task(self._execute(job, pem, password, output_filename))
-
- def _process(self, job: FileJob, pem: str, password: str, output_filename: str) -> None:
+ job.task = asyncio.create_task(
+ self._execute(job, pem, password, output_filename, expected_recipient_fingerprint)
+ )
+
+ def _process(
+ self,
+ job: FileJob,
+ pem: str,
+ password: str,
+ output_filename: str,
+ expected_recipient_fingerprint: str | None = None,
+ ) -> None:
if job.cancel.is_set():
raise stream.OperationCancelled()
info = core.inspect_key_pem_strict(pem)
@@ -255,6 +275,8 @@ def _process(self, job: FileJob, pem: str, password: str, output_filename: str)
if raw is None or algorithm is None or key_type != expected_type:
raise JobError(400, "private_key_failed", "Could not unlock the private key. Check its password and file.")
try:
+ if expected_recipient_fingerprint is not None:
+ core.verify_recipient_fingerprint(raw, algorithm, expected_recipient_fingerprint)
if job.input is None:
raise RuntimeError("Input storage is unavailable.")
if job.mode == "verify":
@@ -289,9 +311,24 @@ def _process(self, job: FileJob, pem: str, password: str, output_filename: str)
finally:
del raw
- async def _execute(self, job: FileJob, pem: str, password: str, output_filename: str) -> None:
+ async def _execute(
+ self,
+ job: FileJob,
+ pem: str,
+ password: str,
+ output_filename: str,
+ expected_recipient_fingerprint: str | None = None,
+ ) -> None:
try:
- await job.lease.run_owned(self._process, job, pem, password, output_filename, on_cancel=job.cancel.set)
+ await job.lease.run_owned(
+ self._process,
+ job,
+ pem,
+ password,
+ output_filename,
+ expected_recipient_fingerprint,
+ on_cancel=job.cancel.set,
+ )
job.state = "cancelled" if job.cancel.is_set() else "complete"
except (stream.OperationCancelled, asyncio.CancelledError):
job.state = "cancelled"
@@ -299,6 +336,13 @@ async def _execute(self, job: FileJob, pem: str, password: str, output_filename:
job.state = "failed"
if isinstance(exc, JobError):
code, message = exc.code, exc.message
+ elif isinstance(exc, core.InvalidRecipientFingerprintError):
+ code, message = "invalid_recipient_fingerprint", "Provide the complete canonical recipient fingerprint."
+ elif isinstance(exc, core.RecipientFingerprintMismatchError):
+ code, message = (
+ "recipient_fingerprint_mismatch",
+ "The public key does not match the expected recipient fingerprint.",
+ )
elif isinstance(exc, core.CryptoDependencyError):
code, message = "backend_unavailable", "The post-quantum backend is unavailable."
elif isinstance(exc, OSError):
diff --git a/crypto_core.py b/crypto_core.py
index 1b33e9d..21696d2 100644
--- a/crypto_core.py
+++ b/crypto_core.py
@@ -9,6 +9,7 @@
import binascii
import ctypes.util
import importlib
+import re
from dataclasses import dataclass
from pathlib import Path
from typing import Optional, Tuple, Dict, Any, Protocol
@@ -81,6 +82,14 @@ class InvalidKeyFormatError(ValueError):
"""Raised when a key file is malformed or semantically invalid."""
+class InvalidRecipientFingerprintError(ValueError):
+ """Raised when an expected recipient fingerprint is not canonical."""
+
+
+class RecipientFingerprintMismatchError(ValueError):
+ """Raised when the loaded public key differs from the expected recipient."""
+
+
@dataclass(frozen=True)
class EncryptedFileMetadata:
"""Non-secret encrypted-container metadata."""
@@ -342,6 +351,26 @@ def get_public_key_fingerprint(key_bytes: bytes, kem_alg: str) -> str:
return f"QE1-SHA3-256:{hashlib.sha3_256(fingerprint_input).hexdigest()}"
+def validate_recipient_fingerprint(expected_fingerprint: str) -> str:
+ """Require a complete canonical fingerprint without silently normalizing input."""
+ if (
+ not isinstance(expected_fingerprint, str)
+ or re.fullmatch(r"QE1-SHA3-256:[0-9a-f]{64}", expected_fingerprint) is None
+ ):
+ raise InvalidRecipientFingerprintError("Provide the complete canonical recipient fingerprint.")
+ return expected_fingerprint
+
+
+def verify_recipient_fingerprint(public_key: bytes, kem_alg: str, expected_fingerprint: str | None = None) -> str:
+ """Compare an independently supplied fingerprint with the actual validated key."""
+ if expected_fingerprint is not None:
+ validate_recipient_fingerprint(expected_fingerprint)
+ actual_fingerprint = get_public_key_fingerprint(public_key, kem_alg)
+ if expected_fingerprint is not None and not hmac.compare_digest(actual_fingerprint, expected_fingerprint):
+ raise RecipientFingerprintMismatchError("The public key does not match the expected recipient fingerprint.")
+ return actual_fingerprint
+
+
def get_public_key_from_private(private_key_bytes: bytes, kem_alg: str) -> bytes:
"""Recover canonical public bytes from validated private-key material."""
_validate_key_material(private_key_bytes, kem_alg, "private")
diff --git a/docs/API.md b/docs/API.md
index 63aef3f..5d82695 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -62,6 +62,8 @@ The custom web UI is served by `api_app.py` at exactly `http://127.0.0.1:`
`backendReady` and `backendMessage` remain in the response for compatibility, while new clients use operation-specific capabilities. A capability `reason` is a safe user-facing summary of an unavailable operation; it is not a raw backend exception.
+`supportsRecipientFingerprint: true` advertises server enforcement of optional expected-recipient fingerprints on single-file encryption and encrypt-mode job starts. Clients offering this check must fetch current health and require this flag before submitting an expectation, including after an authentication refresh before retrying. Older servers may ignore unknown form fields; a stale UI capability is insufficient. The web client rejects unsupported protected requests with `recipient_fingerprint_unsupported`.
+
### Response caching and generated-key custody
Every HTTP response under `/api/*` carries `Cache-Control: no-store` and `Pragma: no-cache`, including JSON successes and errors, authorization or body-limit middleware rejections, unmatched API routes, framework-generated 500 responses, and file downloads. The policy is applied centrally so new API handlers inherit it; static UI responses outside `/api/*` keep their own cache behavior. These directives reduce retention by conforming HTTP caches but do not securely erase browser or process memory.
@@ -78,7 +80,7 @@ All job routes require the existing local authentication and origin checks. IDs
| --- | --- | --- |
| `/api/jobs` | Multipart `mode` (`encrypt`, `decrypt`, `verify`), `filename`, decimal `size` | `{ok: true, job}` reservation |
| `/api/jobs/{id}/upload` | Raw `application/octet-stream`, exactly the reserved size | `{ok: true, job}` |
-| `/api/jobs/{id}/start` | Multipart PEM file `key`, plus `password` for private-key operations | `{ok: true, job}` |
+| `/api/jobs/{id}/start` | Multipart PEM file `key`, plus `password` for private-key operations; optional `expected_recipient_fingerprint` for encryption | `{ok: true, job}` |
| `/api/jobs/{id}/status` | Empty body | `{ok: true, job}` |
| `/api/jobs/{id}/cancel` | Empty body | `{ok: true, job}`; active work may remain `cancelling` |
| `/api/jobs/{id}/clear` | Empty body | `{ok: true}` after cleanup; `409` while work/download owns files |
@@ -110,6 +112,14 @@ The agent `generate-keys` and public `inspect-key` results use the JSON field `p
Compare the complete fingerprint over an independently authenticated channel separate from key delivery. Equality identifies the same validated algorithm label and canonical public bytes, but it is not a certificate, signature, trust chain, proof of identity, proof of private-key control, or protection against a channel that substitutes both the key and comparison value.
+### Expected-recipient encryption
+
+`POST /api/files/encrypt` and encrypt-mode `POST /api/jobs/{id}/start` accept the optional multipart text field `expected_recipient_fingerprint`. It must contain exactly the canonical complete representation above. Absent means no expected-recipient check; a supplied empty value, malformed value, duplicate field, or uploaded file in place of text returns `400 invalid_recipient_fingerprint`. The field is rejected on decrypt/verify jobs rather than ignored.
+
+The service recomputes the fingerprint from the decoded public key used for encryption and compares the complete values before encryption or output creation. Single-file mismatches return `400 recipient_fingerprint_mismatch`. Large-file mismatches fail the job with `error.code: recipient_fingerprint_mismatch`; no encrypted result is published. The expectation is captured at job start and applies to that job's key. Batch encryption sends the same captured expectation with every file request. Existing PEM and ciphertext formats are unchanged.
+
+The CLI exposes the same check as `encrypt --expected-recipient-fingerprint VALUE`; omitted preserves existing behavior, while explicitly empty is invalid. Errors use `invalid_recipient_fingerprint` or `recipient_fingerprint_mismatch` and leave output destinations unchanged. Successful encryption includes `public_key_fingerprint`, the actual recipient key's identifier. Do not derive the expected value from the key being checked: supply the independently authenticated comparison value.
+
### Agent JSON Contract
Successful command output:
@@ -348,6 +358,11 @@ def load_key_pem(
def get_public_key_fingerprint(key_bytes: bytes, kem_alg: str) -> str:
"""Validate canonical public-key bytes and return their versioned SHA3-256 fingerprint."""
+def verify_recipient_fingerprint(
+ public_key: bytes, kem_alg: str, expected_fingerprint: str | None = None
+) -> str:
+ """Return the actual fingerprint; reject a malformed or mismatching supplied expectation."""
+
def get_private_key_public_fingerprint(private_key_bytes: bytes, kem_alg: str) -> str:
"""Derive and fingerprint the public key from already authenticated private-key bytes."""
```
diff --git a/docs/SCREENSHOTS.md b/docs/SCREENSHOTS.md
index 7acda34..759be83 100644
--- a/docs/SCREENSHOTS.md
+++ b/docs/SCREENSHOTS.md
@@ -1,11 +1,29 @@
# Application Screenshots
-These screenshots show the current monochrome custom local web UI. The interface uses progressive disclosure: plain-language task guidance appears first, while suite, format, and key-policy information stays inside **Technical details** until it is needed. The desktop Encrypt capture shows format version 4 and the `ML-KEM-768+X25519-v2` hybrid suite; the mobile Inspect key capture shows responsive workflow navigation and public-key metadata only.
+These screenshots were captured from the real local web app with an available native cryptographic backend, sample files, and a freshly generated demonstration key pair. No API responses or successful operations are mocked. Public fingerprints shown here belong to disposable demonstration keys.
## Custom Web Encrypt Workflow
-
+The demonstration expected fingerprint matches the selected public key. Both the interface and backend reject a mismatch before encryption. In real use, obtain the expected value through an independently authenticated channel. Equality identifies the same key; it does not certify the recipient's identity.
+
+
+
+## Large-file Encryption Result
+
+A real completed encryption job retains its result for an explicit download. The native browser check also decrypts it and compares the recovered bytes with the sample input.
+
+
## Custom Web Mobile Inspect

+
+## Refreshing the Images
+
+Build and start the app using the [README setup instructions](../README.md), with native encryption available. In a second terminal run:
+
+```bash
+npm run ui-native -- --screenshots
+```
+
+This runs the native workflow checks and overwrites these three images at desktop and mobile sizes. Captured pages also undergo automated accessibility and horizontal-overflow checks. Private keys and sample payloads live in an automatically cleaned temporary directory; no private PEM or password appears in the images. `UI_NATIVE_URL` selects another local app URL. If the installed Playwright browser is unavailable, `UI_BROWSER_EXECUTABLE` can select an existing compatible Chromium executable.
diff --git a/docs/screenshots/custom-web-encrypt-workflow.png b/docs/screenshots/custom-web-encrypt-workflow.png
index 9a423ed3b85b59c02abca9078d0208584352d930..7bdfcd544906ad7299b4df01a821a3bec9efe7b0 100644
GIT binary patch
literal 163254
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