Email troy@cornball.ai with anything that looks like a vulnerability. Please do not file a public GitHub issue first.
mx.crypto is a primitives wrapper around vodozemac. It assumes:
- A homeserver may be malicious. Anything that comes back from
/keys/query,/keys/claim, or/syncis untrusted until verified. - A remote device may be compromised. Signed material can satisfy structural checks but originate from the wrong identity. Identity pinning (TOFU + cross-signing) belongs to a higher layer.
- A buggy or hostile peer can ship malformed ciphertext or pre-key
messages.
mx.cryptofails closed: errors propagate, no partial state mutation, no ambiguous return values.
| Component | Version | Status |
|---|---|---|
| vodozemac | 0.10.0 | Contains the fix for the Soatok 2026-02 disclosure of non-contributory Diffie-Hellman acceptance. Verified in the vendored source at src/types/curve25519.rs. |
The full audit walkthrough is in vignettes/security-audit.md.
- ed25519 signatures via
mxc_ed25519_verify(uses vodozemac'sverify_strict— laxer fuzzing paths are not compiled in). - Device-keys structural + self-signature consistency via
mxc_verify_device_keys. - Signed one-time keys via
mxc_verify_one_time_key.
- Identity pinning.
mxc_verify_device_keysreturns the ed25519 key the device claims for itself; it does not check that key against any pre-known trust store. Pin identities at a higher layer. - Cross-signing trust. This package can create, encrypt, restore, and use
Ed25519 signing keys, but it does not query/upload Matrix cross-signing
objects or decide whether a master key is trusted. Those policies belong to
mx.client. - SAS identity decisions. The
mxc_sas_*()primitives provide ephemeral key agreement, commitments, display bytes, and MAC verification. Protocol negotiation, transaction binding, human comparison, timeouts, and persistent trust belong tomx.client; a primitive call alone does not verify a user. - Replay across pickles. Restoring a stale pickle is the caller's responsibility to detect.
mxc_*_pickle() encrypts state under a caller-supplied 32-byte raw
key. vodozemac's pickle format uses a deterministic IV. Practical
implications:
- Use one pickle key per Account / Session. Reusing one key across many unrelated pickles can leak ordering / equality information.
- Derive the key with a KDF from a passphrase you actually have. Don't pass a passphrase directly.
- Treat the pickle blob like any other ciphertext: at-rest storage, no plaintext logging.
- Olm v1 MAC (Soatok's downgrade finding): the wire-format MAC
versioning lives below
mx.crypto. Track the upstream v2 migration. - The
e2e_demo.Rintegration script currently does NOT callmxc_verify_device_keys/mxc_verify_one_time_keyin its broadcast loop. That follow-up is captured in the vignette (section 11). New callers should always call the verify helpers before opening sessions.
When pulling a new vodozemac into the vendor tarball, re-read
vignettes/security-audit.md section 2 and confirm the
contributory-DH check and strict-Ed25519 default are still in place.
Add a NEWS.md line noting the bump.