Cryptography

Everything that travels through Velum is end-to-end encrypted with a hybrid scheme: classical (X25519) plus post-quantum (ML-KEM-768). If one of the two were broken, the other still protects the message.

Identity, wallet and devices

Everything starts from a 24-word recovery phrase (BIP-39, 256 bits). The word list follows the device language; Russian uses the English list because there is no official one.

seed     = BIP39.mnemonicToSeed(phrase)                         64 bytes
identity = HKDF-SHA512(seed, info = "velum/v2/identity")        ed25519
wallet   = HKDF-SHA512(seed, info = "m/44'/7420'/0'/0'/0'")        ed25519
mailbox  = HKDF-SHA512(seed, info = "velum/v2/mailbox")         32 B
  • The identity signs your profile and your device list. Only your contacts know it (via QR code or link); it never goes to the network in plain text.
  • The wallet signs transactions. It's a separate key so nobody can link chats and payments.
  • Each device has its own random key that never leaves it: ed25519 (signing), X25519 and ML-KEM-768 (encryption).

Addresses are vel + base58 of the ed25519 public key (32 bytes). See Wallets and exchanges.

Device list

Each account shares with its contacts, inside encrypted profile messages, a list signed by the identity:

DeviceList = { identity, devices: [{ id, sign, x25519, kem, mailboxSeed, added, name }], ts, sig }

The list is never published on the chain. It allows up to 5 devices. Removing one means signing a new list without it and rotating the mailbox secret.

The hybrid envelope

For each recipient device:

eph            = ephemeral X25519
(kemCt, kemSs) = ML-KEM-768.encapsulate(device.kem)
key            = HKDF-SHA512(ikm = X25519(eph, device.x25519) ‖ kemSs,
                             salt = eph.pub ‖ kemCt,
                             info = "velum/v2/envelope", 32 bytes)
ciphertext     = XChaCha20-Poly1305(key, 24 B nonce)

Format: [version 2][32 eph][1088 kemCt][24 nonce][ciphertext]. It's the same approach as Signal PQXDH and Apple PQ3: an attacker would have to break X25519 and ML-KEM-768 at the same time.

Sealed sender

From the outside, an envelope doesn't say who sent it. Inside are:

  • The content (Payload) signed by the sender's device key.
  • The sender's identity.
  • A certificate: that identity's signature over that device.

So the recipient knows the message comes from that person, and nobody else can know it.

Groups

A group's content is encrypted once with a random key. That key is sealed (with the hybrid scheme) for each device of each member. Every copy looks like a one-to-one message: neither nodes nor an observer can tell it's a group or how many members it has.

Padding

Before splitting, the envelope is padded up to the next fixed bucket: 2 KB, 8 KB, 32 KB, 128 KB, 512 KB, 2 MB or 8 MB. A "hi" and a short voice note can weigh exactly the same. The real size travels inside, encrypted.

Forward secrecy through rotation

Each device rotates its X25519 + ML-KEM pair every 24 hours and announces the new one in its profile. It keeps the previous ones for 7 days to open late messages and then deletes them. If someone stole a device's key today, they couldn't open messages older than a week.

The full triple ratchet (per-message forward secrecy) is on the roadmap.

What attestation protects, and what it doesn't

The app asks Apple for proof (App Attest) that it is the original, unmodified app on a real iPhone. That proof protects the network against spam and reward theft. It is not what protects your messages: even if someone modifies the app, they can't read other people's messages. Cryptography guarantees that.

Algorithms

Use Algorithm
Signatures (identity, wallet, device, nodes) ed25519
Classical key agreement X25519
Post-quantum encapsulation ML-KEM-768 (FIPS 203)
Key derivation HKDF-SHA512
Authenticated encryption XChaCha20-Poly1305
Receipts HMAC-SHA512 and SHA-512
Recovery phrase BIP-39, 24 words
Transaction IDs SHA-256 of canonical JSON