The Book of Rhizo Go back home

Server

The Delivery and Mailboxes chapter explained how we deliver messages without the deliverer learning who’s talking to whom.

The server is designed to be fully anonymized and as close to being a “dumb pipe” that doesn’t know who you are or what you’re sending.

This chapter covers the server in more detail: how the server authenticates requests, secures connections, and stores data - all built around the goal of the server knowing as little as possible.

Overview

There are 2 levels of anonymity:

  1. A Rhizo server account consists merely of a public key, not a username/password. The server doesn’t know who you or anyone is, except that you have a key registered with the server.
  2. We use VOPRF tokens as authentication to allow registered users access the server API without revealing who they are. While someone observing the server can tell when you (anonymous, represented by the public key) are minting the access tokens, they cannot know when you are sending messages, uploading key packages, etc. (that’s the O in VOPRF).

Creating an account

Creation of a server account boils down to registering a new public key with the server. In order to create the account you do need an invitation token.

Once you have registered your key with the sever, authentication with this key is then used only for two purposes: to mint VOPRF tokens and rotate to a new key.

Known risk

There is a known risk with this handling of account registration: when a user invites someone new, an admin/server operator could potentially notice a new key appearing around the same time and infer a correlation to the inviter. This isn’t stored or provable, and can only be done at the moment of invitation, but it is a risk factor currently not fully mitigated and on our list of open questions.

Authentication: VOPRF tokens

To improve privacy, we need the server to authenticate requests without learning who’s making them, so we use VOPRF tokens.

A VOPRF token lets the server sign a token without seeing what it’s signing, also known as a blind signature. You send the server something disguised (blinded), it signs the disguise, and only you can remove the disguise afterward and get a token the server will recognize as valid later, without being able to tie it back to the request where it was issued.

The flow:

  1. The client generates a batch random tokens, “blinds” them (disguises them), and sends them to the server. The request is signed with the account key.
  2. The server signs the “blinded” tokens with its own private key — it never sees the real, unblinded values.
  3. Client “unblinds” signed tokens locally, yielding a batch of tokens that the server will recognize as validly signed on any future request.
  4. Every subsequent request presents one of these tokens instead of the account key.

The server can confirm that a request comes from someone who holds a registered key on the server, without knowing which key issued a given request.

Tokens are one-time use — the server keeps a log of consumed tokens and rejects reuse after that.

Token minting and use are split in time on purpose to prevent correlation: clients mint a pool of tokens in advance and draws from it later, so the time “when you authenticated with your account key” and “when you actually sent something” can’t be linked.

This shows how VOPRF acts as a second anonymization layer, and how we go from anonymous, but identifiable keys to anonymous request. Also worth noting is the fact that it is only possible because access to each mailbox is authorized independently as described in mailbox chapter.

As a result, the server is close to being a fully anonymized “dumb pipe” — it (or anyone taking control of it) can’t target a specific person’s traffic, because it doesn’t know whose traffic is whose.

TLS pinning

The server manages its own TLS keys and runs in a raw public key mode (RFC-7250), with no CA involved.

The client gets the server’s public key as part of the server invite, and the client pins it. Every future connection is verified against that exact key.

This isn’t trust-on-first-use (TOFU). The key is delivered as part of the invite itself, not learned opportunistically on first connect. The rotation is scheduled and announced in advance so clients know to expect a new key rather than mistake it for a MITM red flag.

Why RPK instead of self-signed certificate or CA?

One of our goals is for the server to be really simple to set up in a secure way. We opted to use RPK to simplify the key management, over self-signing certificates.

The desire to simplify secure set up is also why we decided against leaving the server without TLS and relying on the common practice of terminating TLS on some proxy. Another factor that played a role in this choice is that using an external CA does open the unlikely, but possible MITM vector of the CA signing a bogus certificate that the client would trust.

Key management

As one of goals of the project, is to enable any group to organise in secure manner, we strive to simplify setting up the server as much as possible. One of the ways in which this is achieved is that all necessary key management is automated.

There are two independent rotation schedules: one for the TLS key, one for the VOPRF signing key.

TLS key rotation

Server automatically manages it’s key rotation. Each key has not before and not after dates attached to it. When the difference between not after and current date is equal to the preconfigured TLS key announcement period server generates and announces new key with the not before date in the future. This period is usually quite long, so all clients can learn about the upcoming key.

Once the not before date of the upcoming key arrives server stops using the old key in the TLS key exchange and switches to the one announced earlier.

VOPRF key rotation

Token validity is tied to the VOPRF key’s lifetime: when the server rotates that key, it announces the new one and starts signing new tokens with it, but keeps accepting tokens signed with the previous key for a brief transition window so tokens minted just before rotation don’t suddenly become invalid. The exact rotation cadence is configurable and is a tradeoff between how many used tokens need to be stored in the database, and how often clients need to re-mint their tokens.

Key wrapping

The database used by the server doesn’t need to be fully encrypted, but every key stored in it does (all the other data is already encrypted).

That’s why server setup requires configuring a KEK; all other keys the server holds are encrypted under it before being written to the database.

Key wrapping means encrypting one key using another key, in order to securely store it or transmit it over an untrusted channel.

A KEK is a key whose only job is to encrypt other keys. If a database leak happens, it exposes wrapped, unusable keys rather than plaintext secrets, and rotating the KEK doesn’t require re-touching all the underlying data.

The server binary comes with a built-in keygen command to help server admins generate a safe key and append it to config.

Encrypted object storage

We use encrypted object storage for arbitrary file/blob uploads (think S3-like storage). This is currently proxied through the server over gRPC.

gRPC is an open-source framework that abstracts network communication; it enables services to communicate over a network by calling methods on remote servers as if they were local.

The storage backend is filesystem-only today, with S3-compatible support planned.

Access to the encrypted object storage mirrors mailboxes, each object has its own read key and write key pair. The read key lets you fetch the object; the write key lets you manage/delete it. The object is addressed by the hash of its public keys.

Data deletion and minimizing metadata

The server doesn’t store anything it doesn’t use. All deletion is a hard delete, permanently removing a record and minimizing stored data.

There’s no soft-delete or recovery path anywhere in the system. Every object has an expiration date and is automatically irreversibly purged after it.

There are no upload timestamps anywhere, only sequence numbers for ordering. The server does need to know when to delete something, but that value is jittered. A random period of time (up to 24 hours by default) is added so the stored deletion time doesn’t map precisely back to the true expiration date. Without this, someone with access to the deletion timestamp could back-calculate the exact upload time.

We already covered that MLS protects message content and the extra mailbox encryption we employ for anti-correlation due to client-side message fan-out, so the last remaining attack surface is metadata. The server minimizes what it retains, and jitters what it must retain, so metadata can’t be reconstructed from what’s available.