# Security design and assurance

## Scope

Aftercurve Vault protects stored vault contents and exported backups against disclosure from copied ciphertext, subject to master-password strength and correct implementation. Offline transfer is encrypted to a fresh recipient key and requires independent full-fingerprint verification of both the receive request and the resulting package. It does not protect an already-compromised operating system, browser, extension page, update channel, or a website after a password has deliberately been filled there. This release is not independently audited. The ML-KEM dependency also states that it has not received an independent audit; upstream test coverage is not an audit of this product.

## Storage format v1

- A random 256-bit data-encryption key (DEK) encrypts the full vault using Web Crypto AES-256-GCM with a new random 96-bit nonce and 128-bit authentication tag for each encryption.
- The master password derives a wrapping key using Argon2id v1.3: **64 MiB memory, 3 passes, parallelism 1, 32-byte salt, 32-byte output**. This is our fixed browser profile, not the exact parallelism-4 second profile in RFC 9106. Unknown profiles are rejected before derivation, preventing imported files from specifying unbounded work.
- The DEK is separately wrapped with AES-256-GCM under the derived key. Canonical authenticated data binds format, version, vault ID, KDF parameters, and purpose. Vault encryption additionally binds revision and the complete wrapped-key object. Purpose strings separate the two contexts.
- Vault data, including entry titles, URLs, usernames, notes, settings and pending transfer private keys, is encrypted. Only format, algorithm parameters, salts, nonces, ID, revision, size, and ciphertext remain visible in the stored envelope.
- Password changes require the current password, create both a fresh DEK and salt, and reencrypt the entire vault. Old exported backups retain the old password and cannot be remotely revoked or updated.
- Bounds: 10 MiB input files, 6 MiB canonical plaintext, 5,000 entries, 200-character titles, 4,096-character secrets, 65,536-character notes, and fixed cryptographic lengths. Imported objects use strict field allowlists. Unsupported versions fail closed.

## Extension boundaries

Secrets are decrypted in a dedicated extension Web Worker. This reduces accidental exposure in the UI and keeps expensive derivation off the UI thread; it is not a security boundary against malicious extension code. Requested entry details and copied/generated passwords necessarily enter the extension-page process. Keys exist only in the unlocked worker; persistent storage holds ciphertext. Locking terminates the worker and clears rendered inputs. Best-effort buffer wiping cannot guarantee JavaScript engine or operating-system memory erasure.

The timeout is checked on every privileged worker operation and by a periodic timer, using both wall time and a monotonic clock. Changing the clock backwards forces a lock. Hidden-tab, pagehide, reload, and browser restarts require re-unlock. Long-running operations cannot reopen a locked UI: the worker is terminated and UI generation tokens invalidate late replies.

All ciphertext writes pass through a trusted extension-page-only background handler. Writes are serialized and compare the previous envelope's SHA-256 and revision before saving. Stale tabs cannot overwrite newer passwords or imports. Storage failures do not report success. Both local and session storage are restricted to trusted extension contexts.

No permanent host permissions, installed content scripts, external messaging listener, remote code, embedded web frames, telemetry, or fetch calls. CSP sets `connect-src 'none'`. Required permissions: storage for ciphertext, activeTab for a specific user-invoked site, scripting for a one-time top-frame fill, and clipboardWrite for explicit copy actions. Clipboard contents may be read or retained by other software; automatic clipboard erasure is not claimed.

## Autofill

Invoking the toolbar button captures a single-use intent for the source tab's HTTPS origin, bound to the newly opened vault tab. It expires after ten minutes. The saved entry must match the exact parsed origin (including port). Before injection, the background rechecks the source tab; the injected function rechecks the current top-frame origin immediately before modifying fields. It rejects cross-origin form actions, disabled/read-only/hidden inputs, ambiguous password or username fields, and `new-password` fields. No form submission is triggered. This intentionally does not support every website, iframe, shadow DOM, username-first flow, or cross-origin identity provider. A site at the authorized origin can read the credential once filled.

## Offline transfer

See `TRANSFER.md`. ML-KEM-768 is the final FIPS 203 algorithm, not pre-standard Kyber. A fresh receive request has a random ID and a 30-minute lifetime. Public-key material and the request are authenticated by a full SHA-256 fingerprint the sender obtains independently from the recipient. The recipient separately verifies a full SHA-256 fingerprint of the complete encrypted package supplied by the sender through a trusted channel. KEM alone does not authenticate a sender.

HKDF-SHA256 derives the AES-256 key from the KEM secret with a request-hash salt and a versioned domain string. GCM authenticates all transfer header fields, the KEM ciphertext, and destination request. Import requires preview and explicit confirmation. It never overwrites existing records. A successful, atomically saved import consumes and removes the receive key. Ordinary backups exclude receive keys and replay state; restoring one cannot revive a receive request. Restoring an entire old browser profile is outside this rollback model. Expiration assumes a reasonably correct device clock.

## Validation and limitations

The release tests cover a published Argon2id known-answer vector, final ML-KEM vector/interoperability checks, password round trips, encrypted backups, wrong passwords, tampering, hostile lengths/profiles, transfer fingerprints/expiry/replay, key rotation, worker import flows, origin selection, content-script rejection, stale writes, quota failures, and static package/CSP checks. Consult the generated validation report for exact executed checks. Browser installation, lifecycle, and UI behavior must also be verified in actual supported browsers; unit/DOM mocks alone do not prove those behaviors. No independent cryptographic/application audit or extension-store review is claimed.

Independent application review, actual-browser compatibility/lifecycle testing, and signed distribution remain release-assurance work. Users must be able to inspect this status before deciding to entrust secrets to the extension.

## References

- https://www.rfc-editor.org/rfc/rfc9106.html
- https://csrc.nist.gov/pubs/fips/203/final
- https://csrc.nist.gov/pubs/sp/800/38/d/final
- https://developer.chrome.com/docs/extensions/develop/concepts/activeTab
- https://developer.chrome.com/docs/extensions/develop/security-privacy/stay-secure
- https://github.com/paulmillr/noble-hashes
- https://github.com/paulmillr/noble-post-quantum
