False claims in a specification catastrophically fail review. This chapter enumerates every gap between the specification and the code snapshot: audit items not yet in code, items that require user action, cadence trade-offs, optional features off by default, and the open symptoms. Where a discrepancy exists, code is canonical and the spec is the divergence to be fixed.
Why this paper exists
An evaluator reads limitation sections first: a document that admits nothing is hiding something. Publishing this chapter unedited is itself conformance evidence — the gaps below are the same ones an external review would find, already found.
Audit items not yet in code (at the v1.0 snapshot)
| Item | Status | Detail |
|---|---|---|
| Server-contributed transcript binding for relayed handshakes | OPEN — v1.1 | the hybrid combiner already binds every transcript key and ciphertext into HKDF info; the open item is a relay-side ephemeral contribution, defense-in-depth against a precomputing relay |
| Argon2id at the RFC 9106 minimum | OPEN — v1.1 | the spec triple was never the value in code; the vault calibrates on the device to no less than m=128 MiB, t=3, p=4, the local backup runs m=256 MiB, t=4, p=2 and the relay hashes the sign-in secret at m=64 MiB, t=3, p=4 — every number the site prints is read from those constants, and raising the vault floor is the open item |
| Legacy infallible signing wrapper | OPEN — v1.1 |
the deprecated sign() path returns an all-zero
signature on its rare error branch. A zero signature verifies
with probability ~2⁻²⁵⁶ — safe by accident, still a footgun.
Production callers route through the fallible API; removal of
the infallible variant is scheduled
|
| FIPS module gate on internal HKDF utility paths | OPEN — v1.1 | every public crypto entry point passes the operational gate; a few ratchet-internal derivations skip the redundant re-check. Uniform plumbing costs one atomic load per derivation |
Single normative label dump (labels.rs) |
OPEN — v1.1 | the HKDF-label and signature-context catalogs sit in the primitives chapter and the Reviewer FAQ until the normative source file lands |
Items requiring user action
- Operator-key re-pinning is deliberately manual. A legitimate operator rotation requires each client's explicit "Reset Operator Trust" action — so an adversary cannot induce silent re-pinning.
- Safety-number verification is out-of-band by design. A user who accepts a key-change prompt without verifying can be machine-in-the-middled; the UI makes that hard, and the human remains the trust anchor. The check has its own guide.
- Server master-key rotation is a runbook item — recommended monthly; the FSOR guarantee for newly written rows resumes at each rotation.
- Identity-key rotation is never forced. The explicit Settings flow for it is in development; high-threat operators will be advised to rotate quarterly.
Cadence trade-offs
Design defaults tune for typical messaging: PQ ratchet every 100 messages, QRNG injection every 5 (text) / 250 (voice) — the cadences that activate in protocol revision 2. An every-message PQ ratchet is a configuration the design admits but multiplies per-message bandwidth roughly 70× (a ~3,198-byte header versus ~46 bytes) — supported, measured, and not recommended for typical use.
Off by default
- Triple-classical combiner (adds X448): ~10% CPU overhead, wire-format prepared, Cargo-feature gated.
- Fourth-KEM fallback (code-based hardness, NIST IR 8545 class): ~22 KiB handshake overhead, reserved for high-assurance deployments.
- CSfC dual-layer AEAD (AES-256-GCM over ChaCha20-Poly1305): implemented in crypto-core and called nowhere in the iPhone app, so QVault items are sealed once, with ChaCha20-Poly1305.
- FIPS posture: boot-time known-answer tests gate every public crypto entry point in every build, with a few ratchet-internal derivations skipping the redundant re-check (§4 above); a FIPS-mode build feature exists and is not enabled in release builds; the modules are not CMVP-validated.
Seven runtime symptoms were open at the snapshot — from a stale KTS leaf-hash cache rejecting calls to caches surviving panic-wipe. They are listed item-by-item in the source chapter and tracked as active work, not footnotes.
QVault and disappearing messages: the limits the code carries
Checked against the iPhone app's code. Each line is what the code does; when the code changes, this page changes with it.
QVault
- Where the Secure Enclave refuses the key, the PIN is the wall. Custody then falls back to a software envelope whose key-encrypting key sits in the same keychain, so a copied keychain and a guessed PIN open the vault.
- The vault binds the PIN door to your face only once your 24 words are verified. Until then, and whenever it is sealed on an iPhone with no face enrolled, the door rests on a key in this iPhone's Secure Enclave that opens without a face, so a Face ID change does not close it; at the vault's pad the app still asks for your face before every PIN wherever a face is enrolled and Face ID is on for QERYX, while QSeal's sign-in checks its own password before any face, as an item below sets out. Where a face is enrolled, verifying your words binds the door to it at once in a session your PIN opened, or one that set up the vault or set a new PIN; after a session your words or your shares opened, a later PIN unlock binds it, as one does once a face is enrolled on an iPhone that had none. That holds with Face ID turned off for QERYX too, though the door then asks for no face: until the words are verified it is your PIN and this iPhone, and once it is bound it refuses every PIN, a decoy or distress PIN included, until Face ID is on for QERYX again, while your words still open the vault. New words take a door bound to your face back to that key until they are verified, and in such a session they wait until you set a new PIN or open the vault with your PIN.
- A Face ID change closes a PIN door bound to your face. Re-enrolling your face, adding an appearance or removing every face closes it until your 24 words, or a quorum of their shares, open the vault and you set a new PIN. The vault binds a door only once its words are verified, so the words that reopen it are words its owner has checked. While that door is closed, a PIN typed at the vault opens nothing: a decoy PIN opens no decoy, and a distress PIN sends no Silent Distress signal.
- QSeal sets up a vault without showing its 24 words. When no vault stands yet, QSeal sets one up on its own password, which becomes the vault's PIN, and the vault's 24 words are minted without being shown; their owner holds no copy until Seed Custody, in Cold Seal, shows them. Where the vault's pad cannot type that password, the vault opens through QSeal's sign-in instead.
- QSeal's sign-in checks a QSeal-made vault's PIN before any face. That PIN is your QSeal password until you set a new PIN at the vault or set QSeal up again on another password. QSeal's sign-in checks the password itself, on its own count of misses, before any face is asked: a wrong password is refused with no Face ID sheet, and only one QSeal accepts reaches the vault, whose door then asks for your face, as at its pad, before it judges the password. So someone holding the phone can test guesses at that password there without your face, slowed by QSeal's waits after repeated misses, and those misses never count toward the vault's erase.
- Travel Mode stops every PIN. While it is on, no PIN opens the vault, and at the vault a decoy PIN opens no decoy and a distress PIN sends no Silent Distress signal. Only the 24 words, or a quorum of their shares, open it, and a new PIN ends Travel Mode.
- This iPhone only. No item syncs, there is no vault export, and the backup file carries no vault row, so a lost phone is a lost vault; the 24 words reopen it only on the iPhone that holds its recovery record.
- Other parts of the app can read vault rows. They sit in the keychain access group the notification extension and the lock-screen widget also hold; a vault-only group is planned for a later release.
- A copy of the phone shows the vault's shape. The items row keeps the item count, each item's id and its exact sealed size outside any seal; attachments are files of exact size; and the decoy is a second store beside the real one, so a forensic copy shows that it exists. The hidden-layer store is one fixed size, but anyone holding the vault key sees which notes carry a layer.
- The attempt limits are the app's. The wrong-PIN count, its backoff and the wipe run in the app, not in the Secure Enclave, so someone who copies the keychain guesses offline without meeting them.
- Relay sync sends your account password each time you tap SYNC.
- A deletion receipt is a record on this phone. It is kept under a secret on the same phone and cannot show that no older copy of the key list survives in storage.
- No independent audit of QVault or crypto-core has been done, and ChaCha20-Poly1305 and Argon2id sit outside CNSA 2.0.
Disappearing messages
- Setting a timer opens once a two-phone test passes.
- A timer runs while QERYX runs. Setting the clock back does not delay it, but a phone that stays off, or never opens QERYX, keeps a message until QERYX runs again. An unread copy leaves seven days after its timer would have ended, the next time QERYX runs.
- A read without a receipt waits on the relay. A message read with Stealth Mode on or read receipts off, and a sealed group message, keeps its ciphertext on the relay until the offline horizon.
- Photos and files keep their own clock on the relay. A photo, video, file or group voice note is stored apart from its message, encrypted, with an upload record that names the uploader, for up to 45 days after upload. Deleting the message row, or destroying the conversation, does not delete it; the sender’s delete for everyone, or a chat timer’s purge from the sender’s phone, does.
- Some screens are not blacked out. A still screenshot of a photo opened full screen, or of the chat list, captures it; only a chat's rows render black.
- The chat list keeps a timed message's text until the message vanishes.
- Group timer changes are checked by the relay. It refuses a change from a member who is not an admin; the phones cannot yet tell a change an admin sealed from one another member sealed in an admin's name.
- No per-message key at rest. A vanished message's row is deleted from its store file, not shredded under a key of its own; until then its words rest under the phone's data protection.
The v1.1 roadmap
- Land the normative label dump and the relay transcript-binding module.
- Remove the infallible signing wrapper; fallible API only.
- Plumb the FIPS operational gate uniformly through every derivation path.
- Wire the constant-rate padder for messenger transports.
- Begin formal verification: a ProVerif model of the handshake and a Tamarin model of the Q-Ratchet post-compromise claims.
Disclosure
QERYX welcomes coordinated disclosure of anything this spec missed:
support@qeryx.com with SECURITY in the subject. The classes most likely to surface in
review — a timing path we missed, a buffer we forgot to zeroize, a
domain-separation label we did not think of — are named in the source
chapter, because pretending otherwise would fail the review this
document exists to pass. We respond to security mail within 24 hours.
References
-
QERYX Protocol Specification §16 —
docs/SPEC/13-known-limitations.md, snapshot 2026-05-01. - Cryptographic Primitives — the Argon2id close-out record.
- The Hybrid Handshake — the open transcript-binding item in context.