Skip to content

Informal Proposal: Public Key Exchange

ayyghost edited this page Mar 1, 2018 · 1 revision
  • The changes described here have been implemented in master.
  • In this document, "current" in the context of the Cryptodog client refers to stable versions <=2.4.0.

The current format for sending a public key looks like:

{
    "type": "publicKey",
    "text": {
        "recipient1": "<public key content>",
        "recipient2": "<public key content>",
        ...
    }     
}

This type of message is sent in three scenarios: upon joining a room, upon receiving an available presence from a new user, and in response to a publicKeyRequest message. I will address all of these, but we'll focus on the first for now.

Joining a room currently involves rapidly sending many of these messages. Although multiple recipients are allowed in each publicKey body, in practice, the user who just joined ends up sending n messages, each containing one recipient, where n is the number of occupants before they entered.

This is because we don't necessarily know the names of all room occupants immediately upon joining; we have to wait until the server has transmitted all presences to us. Then, the question becomes: how do we know when that is? We could wait a certain amount of time, e.g., 2.0 seconds, before sending a giant message containing copies of our public key for everyone, but that's inelegant because it's dependent on the user's connection. If we're wrong about the timing, some buddies might not get our public key. If we wait too long, we could be missing group messages intended for us.

We could send messages in parcels; say, for three or four recipients at a time, but that would involve keeping track of too much state to be a worthwhile solution.

The obvious answer is just to send a public key each time we receive an available presence from a buddy, which is the current approach. However, this is very noisy. That wouldn't be a huge problem in and of itself, but I have observed it to clash with server-side rate limiting when 10 or more people are in a room (in other words, 10+ public key messages get sent within a few hundred milliseconds). The result: messages exceeding the limit are dropped, and some buddies don't receive your public key.

To mitigate this problem, I propose a new public key format as follows:

{
    "type": "public_key",
    "text": "<public key content>"
}

Notice there is no recipient field by design. The key content, rather than the intended recipient, is the new factor in deciding whether to process a publicKey message: if it's different from the key we already have for that user, process it (of course, still warn the user if a buddy's key suddenly changes). If not, ignore it. The effect here is the same as with the old format. It's just fewer bytes that need to be sent.

Now, you send one copy of the message in two different scenarios: immediately after joining a room, and whenever someone requests your public key. You no longer send your key just because you got a presence from someone. This means the onus is on the person who just joined the room to request keys from everyone. To prevent the same noisiness problem we were trying to solve in the first place, I propose a new format for requesting public keys, as well:

{
    "type": "public_key_request",
    "text": "<nickname of requestee>"
}

name is an optional field here. When omitted, empty, null, etc., everyone should send their public key in response. Thus, one publicKeyRequest message is enough to retrieve keys from the entire room.

With these modifications, no individual user sends more than two messages when someone joins the room. In the context of rate limiting, this is an asymptotic improvement that ensures successful public key exchange, independent of the number of occupants.

Clone this wiki locally