\chapter{DHT Group Chats} This document details the groupchat implementation, giving a high level overview of all the important features and aspects, as well as some important low level implementation details. This documentation reflects what is currently implemented at the time of writing; it is not speculative. For detailed API docs see the groupchats section of the tox.h header file. \section{Features} \begin{itemize} \item Plain and action messages (/me) \item Private messages \item Public groups (peers may join via a public key of group) \item Private groups (require a friend invite for join) \item Permanence (a group cannot 'die' as long as at least one peer retains their group credentials) \item Persistence across client restarts \item Ability to set peer limits \item Group roles (founder, moderators, users, observers) \item Moderation (kicking, silencing, controlling which roles may speak) \item Permanent group names (set on creation) \item Topics (permission to modify is set by founder) \item Password protection \item Self-repairing (auto-rejoin on disconnect, group split protection, state syncing) \item Identity separation from the Tox ID \item Ability to ignore peers \item Nicknames can be set on a per-group basis \item Peer statuses (online, away, busy) which can be set on a per-group basis \item Sending group name in invites \item Ability to disconnect from group and join later with the same credentials \end{itemize} \section{Group roles} There are four distinct roles which are hierarchical in nature (higher roles have all the privileges of lower roles). \begin{itemize} \item \textbf{\verb'Founder'} - The group's creator. May set the role of all other peers to anything except founder. May modify the shared state (password, privacy state, topic lock, peer limit). \item \textbf{\verb'Moderator'} - Promoted by the founder. May kick peers below this role, as well as set peers with the user role to observer, and vice versa. May also set the topic when the topic lock is enabled. \item \textbf{\verb'User'} - Default non-founder role. May communicate with other peers normally. May set the topic when the topic lock is disabled. \item \textbf{\verb'Observer'} - Demoted by moderators and the founder. May observe the group and ignore peers; may not communicate with other peers or with the group. \end{itemize} \section{Group types} Groups can have two types: private and public. The type can be set on creation, and may also be toggled by the group founder at any point after creation. (\emph{Note: password protection is independent of the group type}) \subsection{Public} Anyone may join the group using the Chat ID. If the group is public, information about peers inside the group, including their IP addresses and group public keys (but not their Tox ID's) is visible to anyone with access to a node storing their DHT announcement. \subsection{Private} The only way to join a private group is by having someone in your friend list send you an invite. If the group is private, no peer/group information (mentioned in the Public section) is present in the DHT; the DHT is not used for any purpose at all. If a public group is set to private, all DHT information related to the group will expire within a few minutes. \section{Voice state} The voice state, which may only be set by the founder, determines which group roles have permission to speak. There are three voice states: \begin{itemize} \item \textbf{\verb'Founder'} - Only the founder may speak. \item \textbf{\verb'Moderator'} - The founder and moderators may speak. \item \textbf{\verb'All'} - Everyone except observers may speak. \end{itemize} The voice state does not affect topic setting or private messages, and is set to \textbf{\verb'All'} by default. \section{Cryptography} Groupchats use the \href{https://en.wikipedia.org/wiki/NaCl_(software)}{NaCl/libsodium cryptography library} for all cryptography related operations. All group communication is end-to-end encrypted. Message confidentiality, integrity, and repudability are guaranteed via \href{https://en.wikipedia.org/wiki/Authenticated_encryption}{authenticated encryption}, and \href{https://en.wikipedia.org/wiki/Forward_secrecy}{perfect forward secrecy} is also provided. One of the most important security improvements from the old groupchat implementation is the removal of a message-relay mechanism that uses a group-wide shared key. Instead, connections are 1-to-1 (a complete graph), meaning an outbound message is sent once per peer, and encrypted/decrypted using a session key unique to each pair of peers. This prevents MITM attacks that were previously possible. This additionally ensures that private messages are truly private. Groups make use of 11 unique keys in total: Two permanent keypairs (encryption and signature), two group keypairs (encryption and signature), one session keypair (encryption), and one shared symmetric key (encryption). The Tox ID/Tox public key is not used for any purpose. As such, neither peers in a given group nor in the group DHT can be matched with their Tox ID. In other words, there is no way of identifying a peer aside from their IP address, nickname, and group public key. (\emph{Note: group nicknames can be different from the client's main nickname that their friends see}). \subsection{Permanent keypairs} When a peer creates or joins a group they generate two permanent keypairs: an encryption keypair and a signature keypair, both of which are unique to the group. The two public keys are the only guaranteed way to identify a peer, and both keypairs will persist for as long as a peer remains in the group (even across client restarts). If a peer exits the group these keypairs will be lost forever. This encryption keypair is not used for any encryption operations except for the initial handshake when connecting to another peer. For usage details on the signature key, see the \href{#moderation}{\texttt{Moderation}} section. \subsection{Session keypair/shared symmetric key} When two peers establish a connection they each generate an ephemeral session encryption keypair and share one another's resulting public key. With their own session secret key and the other's session public key, they will both generate the same symmetric encryption key. This symmetric key, which must not be exposed to anyone else, will be used for all further encryption and decryption operations between the two peers for the duration of the session. The purpose of this extra key exchange is to prevent an adversary from decrypting messages from previous sessions in event that a secret encryption key becomes compromised. This is known as forward secrecy. Session keys are periodically rotated to further reduce the potential damage in the event of a security breach, as well as to mitigate certain types of data-based cryptography attacks. \subsection{Group keypairs} The group founder generates two additional permanent keypairs when the group is created: an encryption keypair, and a signature keypair. The public signature key is considered the \textbf{\verb'Chat ID'} and is used as the group's permanent identifier, allowing other peers to join public groups via the DHT. Every peer in the group holds a copy of the group's public encryption key along with the public signature key/Chat ID. The group secret keys are similar to the permanent keypairs in that they will persist across client restarts, but will be lost forever if the founder exits the group. This is particularly important as administration related functionality will not work without these keys. See the \href{#founders}{\texttt{Founders}} section for usage details. \section{Founders} The peer who creates the group is the group's founder. Founders have a set of admin privileges, including: \begin{itemize} \item Promoting and demoting moderators \item The ability to kick moderators along-side non-moderators \item Setting the peer limit \item Setting the group's privacy state \item Setting group passwords \item Toggling the topic lock \item Setting the voice state \end{itemize} \subsection{Shared state} Groups contain a data structure called the \textbf{\verb'shared state'} which is given to every peer who joins the group. Within this structure resides all data pertaining to the group that may only be modified by the group founder. This includes the group name, the group type, the peer limit, the topic lock, the password, and the voice state. The shared state holds a copy of the group founder's public encryption and signature keys, which is how other peers in the group are able to verify the identity of the group founder. It also contains a hash of the moderator list. The shared state is signed by the founder using the group secret signature key. As the founder is the only peer who holds this secret key, the shared state can be shared with new peers and cryptographically verified even in the absence of the founder. When the founder modifies the shared state, he increments the shared state version, signs the new shared state data with the group secret signature key, and broadcasts the new shared state data along with its signature to the entire group. When a peer receives this broadcast, he uses the group public signature key to verify that the data was signed with the group secret signature key, and also verifies that the new version is not older than the current version. \subsection{Moderation} The founder has the ability to promote other peers to the moderator role. Moderators have all the privileges of normal users. In addition, they have the power to kick peers whose role is below moderator, as well as set their roles to anything below moderator. Moderators may also modify the group topic when it is locked. Moderators have no power over one another; only the founder can kick or change the role of a moderator. \subsection{Kicks} When a peer is kicked from the group, he will be disconnected from all group peers, his role will be set to user, and his chat instance will be left in a disconnected state. His public key will not be lost; he will be able to reconnect to the group with the same identity. \subsection{Moderator list} Each peer holds a copy of the \textbf{\verb'moderator list'}, which is an array of public signature keys of peers who currently have the moderator role (including those who are offline). A hash (sha256) of this list called the \textbf{\verb'mod_list_hash'} is stored in the shared state, which is itself signed by the founder using the group secret signature key. This allows the moderator list to be shared between untrusted peers, even in the absence of the founder, while maintaining moderator verifiability. When the founder modifies the moderator list, he updates the \verb'mod_list_hash', increments the shared state version, signs the new shared state, broadcasts the new shared state data along with its signature to the entire group, then broadcasts the new moderator list to the entire group. When a peer receives this moderator list (having already verified the new shared state), he creates a hash of the new list and verifies that it is identical to the \verb'mod_list_hash'. \subsection{Sanctions list} Each peer holds a copy of the \textbf{\verb'sanctions list'}. This list is comprised of peers who have been demoted to the observer role. Entries contain the public key of the sanctioned peer, a timestamp of the time the entry was made, the public signature key of the peer who set the sanction, and a signature of the entry's data, which is signed by the peer who created the entry using their secret signature key. Individual entries are verified by ensuring that the entry's public signature key belongs to the founder or is present in the moderator list, and then verifying that the entry's data was signed by the owner of that key. Although each individual entry can be verified, we still need a way to verify that the list as a whole is complete, and identical for every peer, otherwise any peer would be able to remove entries arbitrarily, or replace the list with an older version. Therefore each peer holds a copy of the \textbf{\verb'sanctions list credentials'}. This is a data structure that holds a version number, a hash (sha256) of all combined sanctions list entries, a 16-bit checksum of the hash, the public signature key of the last peer to have modified the list, and a signature of the hash, which is signed by the private signature key associated with the aforementioned public signature key. When a moderator or founder modifies the sanctions list, he will increment the version, create a new hash of the list, make a checksum of the hash, sign the hash+version with his secret signature key, and replace the old public signature key with his own. He will then broadcast the new changes (not the entire list) to the entire group along with the new credentials. When a peer receives this broadcast, he will verify that the new credentials version is not older than the current version, validate the hash and checksum, and verify that the changes were made by a moderator or the founder. If adding an entry, he will verify that the entry was signed by the signature key of the entry's creator. If a peer receives sanctions credentials with a version equal to their own but with a different checksum, they will ignore the changes if the new checksum is a smaller value than the checksum for their current sanctions credentials. When the founder kicks or demotes a moderator, he will first go through the sanctions list and re-sign each entry made by that moderator using the founder key, then re-broadcast the sanctions list to the entire group. This is necessary to guarantee that all sanctions list entries and its credentials are signed by a current moderator or the founder at all times. \emph{Note: The sanctions list is not saved to the Tox save file, meaning that if the group ever becomes empty, the sanctions list will be reset. This is in contrast to the shared state and moderator list, which are both saved and will persist even if the group becomes empty.} \section{Topics} The topic is an arbitrary string of characters with a maximum length of 512 bytes. The topic has two states: locked and unlocked. When locked, only moderators and the founder may modify it. When unlocked, all peers except observers may modify it. The integrity of the topic is maintained in a similar manner as sanctions entries, using a data structure called \textbf{\verb'topic_info'}. This is a struct which contains the topic, a version, a 16-bit checksum of the topic, and the public key of the peer who last set the topic. The topic lock state is kept track of in the shared state, and may only be modified by the founder. If the topic lock is set to zero, this indicates that the lock is enabled. If non-zero, the value is set to the topic version of the last topic set when the lock was enabled. This allows peers to ensure that the topic version is not modified while the lock is disabled. When the topic lock is \textbf{\verb'enabled'}, the topic setter will create a new checksum of the topic and increment the topic version. They will then sign the topic and version with their secret signature key, replace the public key with their own, and broadcast the new topic\_info data along with the signature to the entire group. When a peer receives this broadcast they will check if the public signature key of the topic setter either belongs to the founder or is in the moderator list, and ensure that the version is not older than the current topic version. They will then verify the signature using the setter's public signature key and validate the checksum. If the received topic has the same version as their own but a different checksum, they will ignore the new topic if its checksum value is smaller than the checksum value for their current topic. When the topic lock is \textbf{\verb'disabled'}, the topic setter will create a new checksum of the topic and leave the version unchanged. They will then sign the topic and version with their secret signature key, replace the public key with their own, and broadcast the new topic\_info data along with the signature to the entire group. When a peer receives this broadcast they will ensure that the topic setter is not in the sanctions list, and ensure that the version is equal to the value that the topic lock is set to, unless the setter has the Founder role, in which case they will ignore the version. They will then verify the signature using the setter's public signature key and validate the checksum. If the peer who set the current topic is kicked or demoted, or if the topic lock is enabled, the peer who initiated the action will re-sign the topic using his own signature key and rebroadcast it to the entire group. If the peer who set the current topic is kicked or demoted, or if the topic lock is enabled, the peer who initiated the action will re-sign the topic using his own signature key and rebroadcast it to the entire group. \section{State syncing} Peers send four unsigned 16-bit integers and three unsigned 32-bit integers along with their ping packets: Their peer count\footnote{We use a "real" peer count, which is the number of confirmed peers in the peerlist (that is, peers who you have successfully handshaked and exchanged peer info with).}, a checksum of their peer list, their shared state version, their sanctions credentials version, their peer roles checksum\footnote{The peer roles checksum is calculated as follows: Make an unsigned 16-bit sum of each confirmed peer's role plus the first byte of their respective public key, then add to this an unsigned 16-bit sum of the sanctions credentials hash.}, their topic version, and their topic checksum. If a peer receives a ping in which any of the versions are greater than their own, or if their peer list checksum does not match and their peer count is not greater than the peer count received, this indicates that they may be out of sync with the rest of the group. In this case they will send a sync request to the respective peer, with the appropriate sync flags set to indicate what group information they need. In certain scenarios a peer may receive a topic version or sanctions credentials version that is equal to their own, but with a different checksum. This may occur if two or more peers in the group initiate an action at the exact same time. If such a conflict occurs, the peer will make the appropriate sync request if their checksum is a smaller value than the one they received. Peers that are connected to the DHT also occasionally append their IP and port number to their ping packets for peers with which they do not have a direct UDP connection established. This gives priority to direct connections and ensures that TCP relays are used only as a fall-back, or when a peer explicitly forces a TCP connection. \section{DHT Announcements} Public groupchats leverage the Tox DHT network in order to allow for groups that can be joined by anyone who possesses the \textbf{\verb'Chat ID'}. Group announcements have the same underlying functionality as normal Tox friend announcements (including onion routing). \begin{code} module Network.Tox.Application.GroupChats where \end{code}