Every cryptographic primitive QERYX instantiates, with its parameter set, byte sizes, and the source citation that pins it. Wire-stable values are byte-locked: changing any of them is a backward-incompatible break. This chapter is the anti-drift rail for every other paper — a primitive that does not appear in the master table does not appear in QERYX.
The master table
| Primitive | Standard | Public | Secret | Output |
|---|---|---|---|---|
ML-KEM-1024 | FIPS 203 (2024) | 1568 B | 3168 B | 32 B shared secret; 1568 B ciphertext, implicit rejection on every decapsulation |
ML-DSA-87 | FIPS 204 (2024) | 2592 B | 4896 B | 4627 B signature; hedged signing, a registered context on every slot |
X25519 | RFC 7748 | 32 B | 32 B | 32 B |
ChaCha20-Poly1305 | RFC 8439 | — | 32 B key | 12 B nonce, 16 B tag |
AES-256-GCM (CSfC outer) | FIPS 197 + SP 800-38D | — | 32 B key | 12 B nonce, 16 B tag |
SHA-512 | FIPS 180-4 | — | — | 64 B |
SHAKE-256 / SHA3-256 | FIPS 202 | — | — | extendable |
HKDF-SHA-512 | RFC 5869 | — | — | PRK 64 B, ≤255×64 B |
Argon2id | RFC 9106 | — | 32 B random salt | 32 B |
HMAC-SHA-256 | RFC 2104 | — | — | 16 B truncated token |
Source citations for every row live in
docs/SPEC/01-primitives.md §2.1, each pointing into
crypto-core/src/.
Asymmetric keys are generated by the FIPS 203 and FIPS 204 key-generation algorithms from the platform random number generator; symmetric keys are drawn from the same generator or derived through HKDF-SHA-512 from established shared secrets, and the iPhone vault's own key is drawn from the generator. In the iPhone app, Argon2id derives the keys from a vault's PIN and from a local backup's passphrase. The vault's 24 recovery words are drawn, not chosen: they carry 256 bits from the same generator, and PBKDF2-HMAC-SHA-256 at 100,000 iterations turns them into the key that seals a copy of the vault key for recovery.
Backend pinning
Every ML-KEM and ML-DSA operation dispatches to an audited library whose validated configuration is not the one QERYX builds link. QERYX's own module boundary is not CMVP-certified, and CAVP validation of the implementation is a laboratory step we have not taken. Two unmaintained third-party PQ crates were removed in the 2026-04 migration for concrete reasons:
- The KyberSlash timing attacks (IACR ePrint 2024/1049; TCHES 2025) recover ML-KEM private keys in minutes on unpatched reference implementations — one removed crate tracked reference C that was never patched past the 2023-12-29 disclosure.
- The other implemented a round-3 signature variant rather than the final FIPS 204 ML-DSA parameterization.
- The pinned backend's ML-KEM kernels are audited secret-independent, and its ML-DSA signs hedged by default per FIPS 204 Appendix D.
Level 5 parameter sets
Both lattice primitives run at NIST security category 5 — the only category CNSA 2.0 accepts for National Security Systems.
| Parameter | ML-KEM-1024 | ML-DSA-87 |
|---|---|---|
| Ring degree n | 256 | 256 |
| Modulus q | 3329 | 8,380,417 |
| Rank | k = 4 | (k, l) = 8 × 7 |
| Classical security | 256 bit | 256 bit |
| Quantum security | 128 bit | 192 bit |
| NIST OID | 2.16.840.1.101.3.4.4.3 | 2.16.840.1.101.3.4.3.19 |
Deterministic ML-DSA signing is never used on mobile: FIPS 204 warns the deterministic variant is vulnerable to fault-injection differential attacks. Every signature draws fresh 256-bit randomness.
Context framing — FIPS 204 §5.2, load-bearing
Every ML-DSA-87 signature on the message, handshake, group-key, call, receipt and manifest paths carries an explicit FIPS 204 §5.2 context string over len(ctx) ‖ ctx ‖ M, and every byte of the context is inside the signed pre-image, so a signature minted for one slot fails verification in another. The signer in code wraps that structure a second time:
pre-image = 0x00 || 0x00 || (0x00 || len(ctx) || ctx || M) // in code: the §5.2 structure wrapped once more
That is self-consistent and domain-separated but not byte-identical to a single-framing FIPS 204 verifier; the migration to single framing is staged behind a dual-accept window that is already open on the verify side. A signature computed under one non-empty context deterministically fails verification under another. Two paths still verify without a context — the relay's control-frame check and the legacy first-message rung — and while the dual-accept window is open a context-bound signature is also a valid context-free signature over its own framed bytes; both are named in Known Limitations and close with the framing flip. Reserved context strings cover the protocol slots: message authentication, handshake transcript, device pairing, the offline release manifest and the witness-quorum attestation, both ESv1 legs, call initiation, the pre-key handshake, KTS tree heads and leaves, FSOR destruction roots, destruction proofs, and the three slots reserved for the QLEAP directory. The full table with citations is in the source chapter.
Labeled HKDF
labeled_hkdf(IKM, label, context, len):
info = "QERYX-v1-" || label || context
return HKDF-SHA-512(IKM, salt = None, info, len)
The "QERYX-v1-" prefix is wire-stable; bumping it is a
session-state break. A boundary-collision-safe variant
(labeled_hkdf_strict) injects a reserved delimiter byte
between label and context for new protocol slots; existing slots stay
byte-locked for forward compatibility — an external review finding
(F-005) closed with exactly this split. The full label catalog is printed in
the Reviewer FAQ.
Argon2id
| Surface | Memory | Passes | Lanes | Class | Source |
|---|---|---|---|---|---|
| Vault (in-app) | m ≥ 128 MiB | t ≥ 3 | p = 4 | enforced floor — device-calibrated upward | crypto-core/src/argon2_password.rs |
| Local backup (Secret Phrase) | m = 256 MiB | t = 4 | p = 2 | enforced constant | Argon2idLocalBackupService.swift |
| Relay sign-in hash | m = 64 MiB | t = 3 | p = 4 | enforced constant, rolling rehash | backend/src/services/mod.rs |
| Secure Enclave wrapper (software custody envelope) | m = 64 MiB | t = 3 | p = 2 | enforced constant — the weakest rung in code | SecureEnclaveKeyWrapper.swift |
Every rung is Argon2id (RFC 9106) with a 32-byte random salt and a 32-byte output. The vault calibrates itself to the device against a time budget and the calibrator clamps its result up to the BALANCED profile, so a manipulated clock can only raise the cost. Every number on this site is read from those constants at build time and names its surface; the spec constant is never quoted as a value in code.
What is deliberately absent
Each absence below is a decision with a reason attached. Nothing here was left out because including it was inconvenient.
- No ECDSA or RSA classical signatures anywhere in the message path. CNSA 2.0 requires ML-DSA-87 for software and firmware signing by 2027; QERYX is already there.
- No ML-DSA-65 fallback. Removed 2026-04-20 per an external audit — there is no negotiation variable to downgrade.
- No Curve25519 EdDSA in the identity path — X25519 is key exchange only; identity signatures are ML-DSA-87.
- No SHA-1, no MD5, anywhere in the dependency tree.
References
-
QERYX Protocol Specification §2 —
docs/SPEC/01-primitives.md, snapshot 2026-05-01. - NIST FIPS 203 (ML-KEM) and FIPS 204 (ML-DSA), 2024.
- RFC 5869 (HKDF), RFC 7748 (X25519), RFC 8439 (ChaCha20-Poly1305), RFC 9106 (Argon2).
- KyberSlash: division timing attacks on ML-KEM reference code — IACR ePrint 2024/1049.
- CNSA 2.0 and FIPS Conformance Posture — the per-line conformance mapping for this table.