Skip to content
Get early access

The fingerprint ceremony

Before your client wraps an environment key to anyone, you and they compare a short string:

XXXX-XXXX-XXXX-XXXX-XXXX

Twenty characters. Read it to each other. If they match, confirm. If they don’t, stop.

Granting access means wrapping an environment’s key to a member’s public key. Your client gets that public key from the server, and the server cannot verify what it is handing you — verifying a wrapped key would mean reading it, which invariant 1 forbids and the architecture makes impossible.

So a server that is actively malicious at that moment could substitute its own public key for your teammate’s and receive a key it can open. Everything else about zero-knowledge holds unconditionally against stored data; this is the one window where the server is a live participant rather than a store.

The fingerprint closes it, by moving the verification to a channel the server does not control: the two of you.

This is the same class of check as a Signal safety number, and for the same reason.

Out of band means out of band. A video call, a phone call, in person, or any channel the server hosting your secrets does not run. Comparing fingerprints in a chat thread that the same infrastructure could tamper with is not a check.

Read it aloud, both directions. Don’t paste it — pasting is how you end up comparing a string against itself.

Compare all twenty characters. Prefix-matching defeats the point.

Crockford Base32 excludes the confusable characters, so there is no I/l/1 or O/0 ambiguity to trip over when reading it out.

Twenty characters is the digest’s first 100 bits.

Getting a substituted key past the ceremony requires a usable X25519 key pair whose domain-separated digest matches the honest key’s first 100 bits — an expected ~2^100 second-preimage search. Birthday collisions do not apply, because the honest key is fixed and honestly generated before an attacker can move.

Small-order points are refused before a fingerprint even exists, which matters: a substituted key that was also a small-order point would make the wrapped key recoverable by anyone, not just the server.

  • Confirming a new member after they accept an invitation
  • Fulfilling a recovery request from a teammate who lost both factors
  • Any grant or rotation that wraps a key to someone you have not verified before

A recovery fulfilment deserves more suspicion, not less. You are being asked to verify a new key for someone whose old key you may already have verified — and the interface says so explicitly, because a server-presented request to re-key an existing member is exactly the shape an attack would take.

What the code guarantees, and what it can’t

Section titled “What the code guarantees, and what it can’t”

Guaranteed: an unverified key cannot be wrapped to. The pin is written only when you confirm, so a failed ceremony leaves nothing behind and the retry starts from first contact rather than passing as already-trusted. This is structural — the raw wrap function is not reachable except through a verified pin.

Not guaranteed: that you actually compared the strings. If you click confirm without reading it to the other person, this protection does not exist. That is listed in the threat model as defended only with your participation, because that is what it is.

Do not confirm. Don’t retry hoping it works — a mismatch is not a glitch.

Contact the other person through a channel you trust, confirm you are talking to the right person, and have them read it again. If it still differs, email security@cendarum.com before granting anything.

A mismatch means either something is broken or something is wrong, and both are worth five minutes.