Specification · for developers

How Umbera encrypts

A technical overview for developers and cryptographers: the standards Umbera is built on, how the layers fit together and what each of them guarantees. The open cryptographic core and its tests let you check the primitives yourself.

Applies to Umbera 2.x (Android, iPhone) and Umbera for computers. November 2026.
01

Threat model

We assume the network and our own server are hostile: they can record, delay, replay and modify traffic, and they serve the key directory. We also assume a future adversary with a large quantum computer who has recorded today’s traffic (“harvest now, decrypt later”).

Goals: confidentiality and integrity of content; forward secrecy and post-compromise security in one-to-one chats; sender anonymity towards the server; no silent key substitution; post-quantum confidentiality for every layer that carries content keys.

Out of scope: a compromised device, and traffic analysis by a global passive observer beyond what padding, randomised group delivery and Tor reduce.

02

Primitives and standards

Only standardised primitives from established, reviewed libraries; no home-made cryptography. One shared core produces identical results on Android, iPhone and computers.

Algorithms and standards
ML-KEM-1024, ML-KEM-768 — FIPS 203
ML-DSA-65 — FIPS 204
X25519 — RFC 7748 · Ed25519 — RFC 8032 · secp256k1
X-Wing hybrid KEM (X25519 + ML-KEM-768)
HKDF-SHA-256 — RFC 5869 · AES-256-GCM · ChaCha20-Poly1305 — RFC 8439
Argon2id — RFC 9106
MLS — RFC 9420 · Merkle transparency log — RFC 6962
03

Identity, safety number and key transparency

Every account has a hybrid signing identity and a hybrid encryption identity. The public address is derived from the post-quantum signing key. The safety number is the same on both sides. Version 2 covers all four identity keys — secp256k1, X25519, ML-DSA-65 and ML-KEM-1024 — and is used when both sides have announced it inside their encrypted messages; with an older version the pair falls back to version 1, which covers the classical keys, and the post-quantum keys are then covered by the transparency log. Once announced, version 2 is pinned: if it disappears, the app warns that the key has changed instead of falling back silently.

The server keeps an append-only Merkle log of identity keys. The app checks the tree head, signed with Ed25519 and ML-DSA-65, and an inclusion proof, so the directory can’t serve a key it didn’t log — including the post-quantum key and the protocol version, which prevents a silent downgrade.

Keys and checks
signing identity = secp256k1 + ML-DSA-65 (both signatures required)
encryption identity = X25519 + ML-KEM-1024
safety number v2 = H( sorted fingerprints of secp256k1 ‖ X25519 ‖ ML-DSA-65 ‖ ML-KEM-1024 ) — identical on both sides
negotiated: v2 only if both sides announce it; otherwise v1 over the classical keys
transparency: tree head signed with Ed25519 ∧ ML-DSA-65, served keys = logged keys, inclusion proof
04

Sealed envelope

Each one-to-one envelope is sealed to the recipient with a classical exchange and a post-quantum encapsulation. The key derivation is bound to a hash of the transcript — the ephemeral key, the ML-KEM ciphertext and the recipient’s keys — as X-Wing does. A full envelope carries a fresh capsule; in between, short envelopes take their keys from a pair secret that both sides have confirmed, moved forward through a one-way chain, so the capsule isn’t repeated in every message. The sender’s identity travels inside, so the server sees neither who wrote nor, from the address, who receives. Envelopes are padded to size classes.

Hybrid sealing
ss_c = X25519(ephemeral, recipient)
ss_q = ML-KEM-1024.Encaps(recipient)
TH = H( ephemeral key ‖ ML-KEM ciphertext ‖ recipient keys )
K = HKDF( ss_c ‖ ss_q , salt = TH , context )
short envelope: key from a confirmed pair secret via a one-way chain; a fresh capsule regularly
envelope = AEAD_K( sender identity ‖ ratchet message ‖ hybrid signature ) → padded
05

Double Ratchet with a post-quantum step

Inside the envelope, a Double Ratchet session changes keys with every message. A post-quantum secret is periodically mixed into the root key, so a conversation recorded today stays closed even if the classical part is broken later, and the session heals after a key compromise.

Key schedule
classical step: (rk′, ck) = KDF_RK( rk, DH )
post-quantum step: (rk′, ck) = KDF_RK( rk, DH ‖ ML-KEM-1024 secret ) — periodically
chain step: (ck′, mk) = KDF_CK( ck )
properties: forward secrecy, post-compromise security, bounded skipped keys
06

Hybrid signatures

Messages, key bundles and device certificates carry two signatures — secp256k1 and ML-DSA-65. A signature is accepted only if both verify, so a forgery requires breaking both schemes.

07

Files

Every file is encrypted with its own random key after padding, so its stored size reveals little. The file key reaches the recipient wrapped with the same hybrid as messages, independently of the channel that carries the file description. The server deletes the file after delivery.

File keys
file = AEAD_fk( padded file ) fk — fresh random key per file
wrap = AEAD_W( fk ), W = HKDF( X25519 ‖ ML-KEM-1024 , salt = TH , file context )
08

Groups (MLS)

Groups use the MLS standard with the X-Wing post-quantum suite. Post-quantum protection covers every operation that distributes keys — invitations, adding and removing members and every epoch change — not only the first key exchange. Existing groups are migrated in place with their history.

MLS member signatures are Ed25519, as the standard defines today. On top of them, every Commit, Proposal and Welcome carries an ML-DSA-65 signature over the MLS frame and the member list, and each sender’s ordinary messages form a hash chain closed by regular ML-DSA-65 checkpoints. Once a member has announced post-quantum signatures, a change in their name without one is rejected. A phone signs with the account key; a linked computer with its own device key, certified by the account.

Every member also refreshes its own leaf regularly with an UpdatePath commit, so the group heals after a key compromise even when nobody joins or leaves. Members on old versions don’t add the ML-DSA-65 signature; their changes are accepted with Ed25519 alone.

Suite and coverage
suite: MLS_256_XWING_CHACHA20POLY1305_SHA256_Ed25519
X-Wing protects every key distribution: Welcome, Commit / UpdatePath (add, remove, epoch change)
application messages: AEAD with keys from the epoch secret
PQ authentication: ML-DSA-65 over every Commit / Proposal / Welcome (MLS frame, member list)
+ regular ML-DSA-65 checkpoints over each sender’s message chain
post-compromise security: every member regularly refreshes its own leaf (Commit with UpdatePath)
migration: in place, same group, history kept; fallback for members without X-Wing
09

Calls and screen sharing

Media is encrypted with DTLS-SRTP between devices, and the relay only forwards ciphertext. Because DTLS key agreement is classical, every audio, video and screen frame is encrypted a second time with keys from a hybrid exchange carried inside the end-to-end encrypted call setup. One-to-one calls, transfers between devices and every pair in a conference get their own keys.

Two layers
layer 1: DTLS-SRTP between devices (relay forwards ciphertext)
layer 2: K_frames = HKDF( X25519 ‖ ML-KEM-1024 , transcript hash )
exchange carried inside the end-to-end encrypted call offer / answer
frame = AEAD_K_frames( frame ) inside SRTP — separate keys per direction and per connection
11

Communities

Every channel has its own pseudonym with its own keys, so channels can’t be linked to each other or to your account. All asymmetric operations are hybrid — Ed25519 + ML-DSA-65 for signatures, X25519 + ML-KEM-1024 for encryption — and private channels encrypt posts, files and metadata end to end.

12

Delivery and metadata

Envelopes go to delivery addresses that only the two parties can compute; they aren’t tied to an account, and once both sides run the new version they are derived from the post-quantum envelope secret as well. From the next release the address of a pair also changes every week; the boundary is the same for everyone, so a change doesn’t single out a conversation. The switch is negotiated inside the encrypted conversation, and the app keeps listening on the neighbouring weeks, so nothing is lost at the boundary or while a device is offline. With a contact on an older version, and for linked computers, the address doesn’t change yet. An app checks all its mailboxes over one connection, so a server watching live can group the addresses of one device. Push wake-ups go to a per-device wake handle that isn’t linked to any mailbox address; the handle is shared with contacts only inside end-to-end encrypted messages. Anonymous endpoints carry no account token. Sizes are padded, and a group message leaves as separate envelopes in random order with small random delays. The connection to the server uses TLS 1.3, and with Tor turned on the server doesn’t see the IP address; without Tor it does.

13

On the device

Local databases are encrypted, and their keys are held by the system keystore of each platform. Key backups and chat exports are encrypted with a key derived from your password by Argon2id.

14

Why it holds

Every layer that protects content keys combines a classical and a post-quantum secret, so it stays secure as long as at least one of them does. Signatures are a conjunction: a forgery needs both schemes broken.

There is no independent audit yet. We have applied for one and will publish the report in full. If you find a problem, write to the contacts in SECURITY.md — we fix it, ship the fix and say what was wrong.

15

Check it yourself

The cryptographic core and its tests are open source under Apache-2.0. Reference vectors pin every primitive byte for byte. On the Proof page you can watch the tests run and see what the relay records in Wireshark.

Run the tests
$ git clone https://github.com/UmberaAPP/UMBERA && cd UMBERA
$ cargo test