-
Notifications
You must be signed in to change notification settings - Fork 47
Private Rooms
Our concept of privacy means more than just protection from external spying and influence. We also want to extend a reasonable amount of privacy to users from ourselves. At the same time, we want to make chat easy for users by keeping it centralized and maintaining a healthy social environment.
The security model outlined in this document offers a potential spectrum of privacy options. At one extreme, we could provide coordination of cryptographically secure chat rooms with zero knowledge of the keys required to unlock the content of the room. Or we can let in anyone who knows the passcode. The middle ground that is probably more than good enough for most private rooms involves minimal knowledge of keys on the server, tightly bound to live sessions on the servers with auditing of all access by the service provider.
This model assumes a hierarchical administration of a room, where all privileges must be granted by a privileged party. These privileges are represented by capabilities, which are generally access keys encrypted by a secret known only to the client.
The key management service, or KMS, is a (probably external) service that is the secure source of all of our keys. In addition to being a secure source of keys and random data, the KMS also logs every interaction made with it.
A site master key is a symmetric cipher key belonging to the site from which all other keys can be derived. There is always one primary site master key used for generating all new keys. Older site master keys may exist for key rotation purposes. The site master key is hidden within the KMS. We can encrypt and decrypt data with it, but we never possess a copy of it outside the KMS.
A room key is a symmetric cipher key for accessing messages posted in the room. It is derived from a site master key. There may be multiple room keys over the life of a room, as the ability to rotate room keys is a must. Older keys may be preserved for accessing older messages. We never generate or access the room key directly; it is returned to us by KMS encrypted with our site master key.
A shared secret is some token or knowledge shared between the ownership of a room and a user or client. It may take the form of a passcode, access token, etc.
A client key is either a symmetric cipher key derived from a shared secret with the client, or the private key of a client's asymmetric keypair. The lifetime of a client key is independent of the lifetime of a room or message key.
A capability is a room key paired with some context. The room key is encrypted by a client key, and that encrypted key is the capability identifier. The relation between the identifier and context grants access to room messages to whoever knows or can provide the client key.
A room nonce is a random value generated alongside a room key, to be used for locating capabilities.
We do not store our site master key ourselves. Instead it is managed as a customer master key (CMK) in AWS Key Management Service (KMS).
A room key is a 128-bit data key generated by KMS. For hard privacy, we would never access this key directly. Our initial implementation (and, probably most use cases) will allow the server to briefly hold a copy of the key in plaintext for the immediate purpose of granting access to the room. All such grants will be logged by the KMS, and should be available to room owners.
When a room is locked, we'll ask the KMS to generate a key for us. The KMS can return it to us directly in encrypted form. To access its plaintext, we must go through the KMS, which will log the access. Once generated, we store the encrypted room key in the database is a property on the room. We will also kick off a background process to re-encrypt the room's log, and start prohibiting posts to the room from clients that can not prove they hold a grant.
Capabilities provide the ability for authorized clients to securely access the plaintext of messages without interacting with Amazon KMS. A capability is simply the room key encrypted by a client's key, associated with some context and an authentication code.
Much like the server decrypts the room key by applying its master key (via KMS), the client decrypts the room key by applying its own key. If the client chooses server-side decryption of room content, then the room key will be held in memory and theoretically accessible to the service provider, albeit not too trivially. Or, it may be retained client side, as long as the client can handle the room encryption scheme.
The server needs to be able to determine if there is a grant for a client, before allowing the client to publish to the room. We need a way to determine if a capability for a given client key exists. For this purpose we use the room nonce, 128 bits of random data generated when the current room key was generated. When we grant a capability to a client key, we encrypt the nonce with the same key to produce an identifier to store the capability under.
When a client requests access to a private room, we simply apply the client key to the nonce again to determine the capability identifier to look up. If the client is handling crypto duties, then the server will send the nonce to the client as a challenge, expecting the capability id as a result. If this resolves to a capability, it must still decrypt using the same client key (the capability's authentication code will verify this) before this access is granted.
An anonymous grant takes the form of some sort of access code. This may be randomly generated (for bots), or a password (for humans). The SHA-256 hash of the access code provides the symmetric client key for encrypting and decrypting the capability. This access code must be provided at the time of the grant, and anytime the client wishes to access the capability.
When we record the encrypted capability, we store it under an identifier that can be derived from the client key and room nonce. This allows us to associate capabilities with the generation of room key that they grant access to. (This may not be necessary).
When we build our account system, we will generate an asymmetric keypair for each account. Keypair management is described in a document I haven't written yet (TODO!).
Granting a capability with a keypair is similar to an anonymous grant, it just uses a different encryption scheme (RSA-OAEP). A capability is encrypted with the account's public key, and accessed through the account's private key.
The administrator of a room needs to be able to view and revoke all of the grants that are currently valid. These means there must be an index between capability identifiers and a room key. This table should contain denormalized data about who or what these grants belong to (passcodes and accounts). The columns of this table should be encrypted with the room key so as not to expose room participants immediately to the service provider.
Messages (and other sensitive room content) are encrypted by the room key using AES-128 with the GCM cipher. The implementation of this cipher that we're using requires a unique 12-byte nonce for each message. It can be a predictable value, but it's very important that it's unique. We'll use the snowflake of the message (padded with four bytes of zeros) for encrypting messages. Other data types will similarly use their unique keys. It's very unlikely that two snowflakes will ever collide, and even more unlikely that they'll do so in the same room.
- Site Master Key - stored in Amazon KMS
- Room Key - 128 bits, stored encrypted in our DB, can be decrypted by Site Master Key (audited)
- Room Nonce - 128 bits, not secret, randomly generated by Amazon KMS (audited)
- Client Key - either 256 bits, derived from SHA-256 hash of access code, or an account private key