← Back to notes
Flutter & Cross-Platform 2026-06-05 08:17 4 min read Local copy

I built a privacy messenger for activists in Flutter — and had to invent my own Sealed Sender

I built a privacy messenger for activists in Flutter — and had to invent my own Sealed Sender
Pontus
Pontus

Posted on Jun 5

I built a privacy messenger for activists in Flutter — and had to invent my own Sealed Sender
#security #cryptography #flutter #opensource

I built a privacy messenger for activists in Flutter — and had to invent my own Sealed Sender

Why I built Spectre

I don't like surveillance. My belief is that people should be able to communicate freely without the fear of repercussion for opinions that don't align with the state narrative. That's the entire motivation behind this project. Spectre is an end-to-end encrypted messenger built specifically for activists and journalists that operate in high risk environments.

The threat model is not complicated: state actors, corporate surveillance and forensic device seizure. I also believe that when people come together and build stuff in the open, all can gain from it. So therefore I'm using the Signal Protocol, the gold standard for encrypted messaging. The client is built in Flutter since it runs on iOS, Android, Linux and macOS from a single codebase.

The problem I hit

AI is a super useful tool but after some thinking and discussion with the instance we hit a wall. The Dart Signal Protocol library (libsignal_protocol_dart) ships no sealed sender implementation. The Signal Protocol has a feature called sealed sender which hides the metadata of who is sending messages to whom. The relay only sees the recipient and an opaque encrypted blob, never the sender. For activists and journalists operating against state-level adversaries, metadata alone can be dangerous enough to get someone killed.

What I built

It works like this.

When Alice wants to send Bob a sealed message, her client generates a temporary ephemeral key pair. It performs an X25519 key exchange with Bob's long-term identity key to derive a shared secret. That secret goes through HKDF-SHA256 to produce an encryption key, with context binding to prevent the key being reused in other contexts. The actual message is then encrypted with ChaCha20-Poly1305.

Inside that encrypted envelope sits not just the message but also a certificate issued by the relay. That certificate says "this message came from a legitimate user on this relay" — signed with Ed25519 — without revealing which user. The relay signs it but can't see inside the envelope. Bob's client decrypts the envelope, verifies the certificate signature, and learns the sender identity from the certificate.

The relay sees: recipient, encrypted blob, timestamp. Nothing else. That's the goal — and that's what needed to be built from scratch.

The honest limitation — the relay-as-CA question

Here's where I have to be transparent about a design tension. The relay issues the sender certificates — which makes it a certificate authority. A malicious relay operator could therefore forge a certificate, making a message appear to come from someone it didn't, or stripping sender attribution entirely.

The defence is out-of-band safety number verification — the same mechanism Signal uses. If you compare safety numbers with your contacts via a separate channel, you'd catch a relay tampering with attribution. Spectre has a full safety number verification screen and a key change warning banner for exactly this reason.

But — and this is important — I need a cryptographer to confirm that framing is sound. Is "relay as CA, with sender trust resting on out-of-band fingerprint verification" a defensible position? Or does the CA need to be decoupled from the relay entirely?

What's been reviewed so far

The construction has gone through two internal review passes. Key findings that have been addressed:

  • The key derivation was hardened
  • A binding was added to prevent message substitution attacks
  • Identity key pinning implemented — warns when a contact's key changes
  • Cross-language compatibility tests added between the Go relay and Dart client
  • Rate limiting on certificate issuance added to the relay
  • Replay protection via persistent message deduplication implemented

The construction has been judged sound at the primitive level by the internal passes. What it hasn't had is an independent external cryptographer review the overall design — specifically the relay-as-CA trust model.

What I'm asking for

Why am I posting to dev.to? I've already posted on Cryptography Stack Exchange and Hacker News but the more eyes the better.

The full construction, threat model, all findings and open questions are documented here:

The specific question I most need answered is the relay-as-CA question — is the design sound given the out-of-band verification defence, or should the CA be decoupled from the relay?

Spectre is not recommended for real users yet. That's a deliberate decision — shipping a privacy tool for at-risk people with unreviewed bespoke crypto would be irresponsible. The sealed sender review is the gate before any public launch.

If you have a cryptography background and are willing to take a look, I'd genuinely appreciate it.

Panic Wipe

Top comments (0)

Subscribe

For further actions, you may consider blocking this person and/or reporting abuse