Audit scope
A brief for an external reviewer. Auditors bill by the hour and spend the first of them working out what the system is and what is worth attacking — this document is an attempt to give that away for free rather than pay for it twice.
Nothing here asks for a conclusion. Where this project believes something is sound, that belief is stated so it can be attacked directly.
1. What the system is
Three components, one repository:
engine/src/ |
JavaScript | Reference cryptographic engine. Normative where implementations disagree. |
Sources/E2E/ |
Swift (CryptoKit) | iOS engine. Checked against the reference in both directions. |
server/ |
Swift (Vapor) | The relay. Holds no key that opens an encrypted message. |
There is no Android implementation. There is no browser client — a web client would be re-delivered by the server on every load, which is the property the design exists to avoid needing to trust.
2. What is in scope
In rough order of what would hurt most if wrong:
- Session establishment — X3DH and its post-quantum hybrid (spec §5, §12). Key confusion, missing domain separation, associated-data mismatches.
- The Double Ratchet (§6, §13). Nonce reuse, chain-key reuse across a ratchet step, out-of-order and skipped-message handling.
- Token authentication and proof-of-possession (§8,
Token.swift). Replay, cross-device use, cross-relay use, and the single-use proof cache. - Sealed sender (§14). Whether a sealed submission leaks its sender through any path — including rate-limit state, attachment rows, and error timing.
- Key transparency (§19). Whether inclusion and consistency verification can be made to accept a substituted key.
- Post-quantum identity (§20). Hybrid verification, and whether the downgrade pin can be evaded.
- At-rest encryption
(
AtRestCrypto.swift). Applies only to conversations with encryption deliberately turned off.
3. What is explicitly out of scope
Not because it is safe, but because it is known:
- Metadata. The relay sees recipient, timing,
approximate size and source address.
/metadatadocuments what is stored and for how long. - Group rosters. Server-visible by design; §22 records the alternative.
- Compromised endpoints. Keys live on devices; malware on one reads plaintext.
- Post-quantum authentication below iOS 26. No
MLDSA65, so those devices are classical-only and say so.
4. Trust boundaries
- Client ⇄ relay. The relay is assumed hostile. It distributes prekey bundles, so it is the natural place to attempt key substitution — §19 exists to make that leave evidence, and §19.4 gossip is what detects a fork.
- Issuer ⇄ relay. The issuer's verification key is configuration, never fetched. A relay that fetched it over HTTPS could be handed a key by a compromised issuer.
- Device ⇄ device. Peers authenticate through the ratchet. Identity changes are pinned and surfaced rather than accepted silently.
5. Assets
Ordered by consequence of disclosure:
- Device identity private keys (
ikSig,ikDH) — impersonation. - The issuer signing seed — mints tokens the relay accepts for any device.
- Ratchet state — decrypts the messages of the sessions it covers.
- The at-rest key — opens stored non-E2E message bodies.
- Delivery keys — permit sending sealed to a recipient; not confidentiality.
6. Where to start reading
engine/docs/SPEC.md— the protocol, primitives through REST contract.engine/src/e2e.js— the reference engine.server/Sources/FlamenetRelay/Routes.swift— §8 handlers, including the one deliberately unauthenticated route.server/Sources/FlamenetRelay/Token.swift— token and proof verification.engine/test/vectors/protocol-v1.json— published vectors, reproduced by both engines.
7. Claims we would like attacked
Stated as falsifiable propositions rather than assurances:
- A relay cannot read the contents of an encrypted conversation.
- A relay cannot serve a device key it has not first committed to the log, without two clients who talk to each other detecting it.
- A sealed submission's stored record does not identify its sender, and no other row does either.
- A token stolen from one device does not authorise action as another device, nor at another relay.
- A device that has published a post-quantum identity cannot be presented as classical to a client that has already seen it.
8. Known-unsound, do not spend time confirming
- The at-rest key is held by the operator. Non-E2E conversations are readable by whoever runs the relay. Any marketing claim to the contrary is a bug in the copy, and the copy has been corrected.
- Sealed sender is not anonymity. The connection is still observed.
- Key transparency without client gossip detects a server that changes its mind, not one that was never honest. Gossip is implemented on web and iOS; a fork along social boundaries still evades it.
9. Test surface
engine/ — five suites, run with npm test.
The relay suite boots the compiled binary and drives §8 over HTTP,
negative cases included. The interop suite compiles the iOS engine from
the app's own sources, so a drift between engines fails here rather than
on a phone.
A green run is not evidence of security. It is evidence that the specific things we thought to check still hold.