Teams Crypto

Each team has a set of per-team keys (PTKs). The specifics are very similar to PUKs. A user creates a set of PTKs when posting a team.root or a team.subteam_head link, as described in Team Sigchain Details. And, a user rotates these keys to a new a generation in a team.rotate link.


The specifics of each PTK generation are as follows:

  • A user generates a 32-byte random seed s
  • She computes e = HMAC(s, "Keybase-Derived-Team-NaCl-EdDSA-1") and uses this value as the secret key for an EdDSA signing key. She then computes the public half, yielding keypair (E, e).
  • She computes d = HMAC(s, "Keybase-Derived-Team-NaCl-DH-1") and uses this value as a secret key for a Curve25519 DH encryption key. She then computes the public half, yielding keypair (D,d)
  • She computes c = HMAC(s, "Keybase-Derived-Team-NaCl-SecretBox-1") to use as a secret key in NaCl secretbox when encrypting previous seeds s. (See below for details)

Where HMAC is computed with SHA512, and truncated to the first 32 bytes.

This process is repeated at every generation i. At generation i, the keys Ei and Di are signed into the team's public sigchain. Whenever a new user is added, si is encrypted for the user's public PUK DH key. This make si available on every device for the user. This box is written to the main the DB. The current si should have a box for every team member, whether implicit or explicit.

When a team member revokes a device, or a team member is reset, or a user leaves or is removed from a team, the PTK must rotate. This can happen lazily. The new PTK keys are encrypted for all remaining members, and the new public key halves are written into the team's sigchain. Also, whenever the key rolls over, the previous seed si is encrypted with ci+1 via NaCl's SecretBox symmetric encryption, just as with users. See the description of Cascading Lazy Key Rotation for more details about how this rotation is orchestrated.

Application Keys

Team members derive application keys from the shared seeds si described above:

  • For KBFS: HMAC(si, "Keybase-Derived-Team-KBFS-1") ⊕ Si,KBFS
  • For Chat: HMAC(si, "Keybase-Derived-Team-Chat-1") ⊕ Si,CHAT

That is, the key is the XOR of a key derived from the team-shared secret si, and a server-stored 32-byte random mask. For the given team, all readers, writers, owners, and explicit admins can see these 32-byte masks. The server withholds them from implicit admins, preventing them from viewing the chats or files encrypted with those keys.

In chat, all messages are encrypted using NaCl's secret box primitive, with the key derived as above. When the key is rotated, new chats are encrypted with the new key, but old chats aren't reencrypted.

For KBFS, these derived keys take the place of sf,0 ​​