The problem
When Alice's phone asks for Bob's keys, the server answers. A malicious server could answer with keys it controls, receive the album key meant for Bob, and pass a copy along so nothing looks wrong. Signatures inside the bundle do not help, because the server signs its own bundle. Miuchio answers this with pins, a signer check on every delivery, and safety numbers.
Two kinds of pins
A pin is a stored copy of the identity key a phone accepted for someone. Pins are kept per album and member token, encrypted under the device cache key. There are two kinds, because seeing someone and trusting them to hand you keys are different things:
- Roster pins record the key shown for each member in the member list. Epoch changes use them: a new album key is only wrapped to a bundle whose key matches the pin, and a member with no pin stops the change before a key is even generated.
- Signer pins record which key may sign key deliveries for a member token. Appearing in the member list never grants this.
When Alice invites Bob, her phone pins the key it just wrapped the album keys to. It never overwrites an existing pin, so a returning member keeps their earlier baseline.
The signer check
Before a phone installs a delivered album key, it decides whether the key the server presents for the sender may sign:
- If the sender is this phone's own member token, the key must equal this phone's own identity key.
- If the sender has a signer pin, the key must match it exactly.
- If the sender has no pin, the key is accepted only when this phone holds no keys at all for the album, which is a genuine first join. It is pinned only after the signature verifies.
- Otherwise the delivery is refused and the album is marked blocked, with the reason shown.
An example. Bob is already in Picnic with a signer pin for Alice. The server tries to slip in a key of its own:
server presents sender = Alice's token, IK = Mallory's key
Bob's phone has a signer pin for Alice's token
Mallory's key != pin -> refused, album blocked
server retries sender = a brand new token, IK = Mallory's key
Bob's phone holds keys for Picnic, so this is not a first join
-> refused, album blockedThe first join rule depends on what the phone holds, not on anything the server says. If it trusted a server flag like "you just joined", a server could set that flag on an established album and reopen first use trust. While the creator's own epoch 0 has not installed, every other signer is refused. Key comparisons run in constant time.
Safety numbers
The idea comes from Signal. Miuchio's number is:
safety_number = first 30 decimal digits of SHA256(sort(IK_A, IK_B) || album_id)
shown as 12345 67890 12345 67890 12345 67890- Sorting makes the number the same on both phones.
- Including the album ID gives the same two people a different number in each album they share.
- Your own key comes from the private key on your phone, never from the server. If the server supplied both sides it could make any pair match.
- Marking someone verified is stored against the pair of identity keys. Each album shows a different number, but every one is computed from the same two keys, so a match in one album proves them for all of them, and the person shows as verified in every album you share. In the album where you compared, it also sets their signer pin, which is the only way besides a first join to grant signing authority.
If a pinned member's key changes, the app shows a warning on that member. The server refuses a new identity key for an existing account, so today a change means the server presented a different key, not that a friend reinstalled. The app still warns rather than locking anyone out, because backups and second devices will make honest key changes possible later. Deliveries signed by the changed key are refused until you compare numbers. Scanning a QR code instead of reading digits is not built yet.
What this cannot solve
- First contact. The first key the app sees for someone is trusted on first use. If the server substitutes a key at that moment, only comparing safety numbers reveals it.
- Invites. An invite does not compare the invitee's key with an existing pin before delivering the album keys. The phone pins the key it wrapped to afterwards, and never overwrites an earlier pin. If that key was substituted, a later mismatch with the real key shows a warning, but a warning cannot take back keys that were already delivered.
- No key transparency. There is no public log of identity keys that would make substitutions detectable at scale.