/work/toxcore/group_moderation.h
Line | Count | Source |
1 | | /* SPDX-License-Identifier: GPL-3.0-or-later |
2 | | * Copyright © 2016-2020 The TokTok team. |
3 | | * Copyright © 2015 Tox project. |
4 | | */ |
5 | | |
6 | | /** |
7 | | * An implementation of massive text only group chats. |
8 | | */ |
9 | | |
10 | | #ifndef C_TOXCORE_TOXCORE_GROUP_MODERATION_H |
11 | | #define C_TOXCORE_TOXCORE_GROUP_MODERATION_H |
12 | | |
13 | | #include <stdbool.h> |
14 | | #include <stdint.h> |
15 | | |
16 | | #include "DHT.h" |
17 | | #include "attributes.h" |
18 | | #include "crypto_core.h" |
19 | | #include "logger.h" |
20 | | #include "mem.h" |
21 | | |
22 | | #ifdef __cplusplus |
23 | | extern "C" { |
24 | | #endif |
25 | | |
26 | 6.87k | #define MOD_MODERATION_HASH_SIZE CRYPTO_SHA256_SIZE |
27 | 25.5k | #define MOD_LIST_ENTRY_SIZE SIG_PUBLIC_KEY_SIZE |
28 | 3.16k | #define MOD_SANCTION_HASH_SIZE CRYPTO_SHA256_SIZE |
29 | | |
30 | 785 | #define TIME_STAMP_SIZE sizeof(uint64_t) |
31 | | |
32 | | /* The packed size of a Mod_Sanction_Creds */ |
33 | 1.32k | #define MOD_SANCTIONS_CREDS_SIZE (sizeof(uint32_t) + MOD_SANCTION_HASH_SIZE + sizeof(uint16_t) +\ |
34 | 1.32k | SIG_PUBLIC_KEY_SIZE + SIGNATURE_SIZE) |
35 | | |
36 | | /* The packed size of a Mod_Sanction */ |
37 | 499 | #define MOD_SANCTION_PACKED_SIZE (SIG_PUBLIC_KEY_SIZE + TIME_STAMP_SIZE + 1 + ENC_PUBLIC_KEY_SIZE + SIGNATURE_SIZE) |
38 | | |
39 | | /* The max size of a groupchat packet with 100 bytes reserved for header data */ |
40 | 1.18k | #define MAX_PACKET_SIZE_NO_HEADERS 49900 |
41 | | |
42 | | /* The maximum possible number of moderators that can be sent in a group packet sequence. */ |
43 | 874 | #define MOD_MAX_NUM_MODERATORS_LIMIT (((MAX_PACKET_SIZE_NO_HEADERS) / (MOD_LIST_ENTRY_SIZE))) |
44 | | |
45 | | /* The maximum number of moderators that we allow in a group: 100 */ |
46 | 874 | #define MOD_MAX_NUM_MODERATORS ((MOD_MAX_NUM_MODERATORS_LIMIT / 16) + 3) |
47 | | |
48 | | /* The maximum number of sanctions that be sent in a group packet sequence. */ |
49 | 308 | #define MOD_MAX_NUM_SANCTIONS_LIMIT (((MAX_PACKET_SIZE_NO_HEADERS - (MOD_SANCTIONS_CREDS_SIZE)) / (MOD_SANCTION_PACKED_SIZE))) |
50 | | |
51 | | /* The maximum number of sanctions that we allow in a group: 30 */ |
52 | 606 | #define MOD_MAX_NUM_SANCTIONS (MOD_MAX_NUM_SANCTIONS_LIMIT / 12) |
53 | | |
54 | | typedef enum Mod_Sanction_Type { |
55 | | SA_OBSERVER = 0x00, |
56 | | SA_INVALID = 0x01, |
57 | | } Mod_Sanction_Type; |
58 | | |
59 | | typedef struct Mod_Sanction_Creds { |
60 | | uint32_t version; |
61 | | uint8_t hash[MOD_SANCTION_HASH_SIZE]; // hash of all sanctions list signatures + version |
62 | | uint16_t checksum; // a sum of the hash |
63 | | uint8_t sig_pk[SIG_PUBLIC_KEY_SIZE]; // Last mod to have modified the sanctions list |
64 | | uint8_t sig[SIGNATURE_SIZE]; // signature of hash, signed by sig_pk |
65 | | } Mod_Sanction_Creds; |
66 | | |
67 | | /** Holds data pertaining to a peer who has been sanctioned. */ |
68 | | typedef struct Mod_Sanction { |
69 | | uint8_t setter_public_sig_key[SIG_PUBLIC_KEY_SIZE]; |
70 | | |
71 | | // TODO(Jfreegman): This timestamp can potentially be used to track a user across |
72 | | // different group chats if they're a moderator and set many sanctions across the |
73 | | // different groups. This should be addressed in the future. |
74 | | uint64_t time_set; |
75 | | |
76 | | uint8_t type; |
77 | | uint8_t target_public_enc_key[ENC_PUBLIC_KEY_SIZE]; |
78 | | |
79 | | /* Signature of all above packed data signed by the owner of public_sig_key */ |
80 | | uint8_t signature[SIGNATURE_SIZE]; |
81 | | } Mod_Sanction; |
82 | | |
83 | | typedef struct Moderation { |
84 | | const Memory *_Nonnull mem; |
85 | | const Logger *_Nonnull log; |
86 | | |
87 | | Mod_Sanction *_Nullable sanctions; |
88 | | uint16_t num_sanctions; |
89 | | |
90 | | Mod_Sanction_Creds sanctions_creds; |
91 | | |
92 | | uint8_t *_Nullable *_Nullable mod_list; // array of public signature keys of all the mods |
93 | | uint16_t num_mods; |
94 | | |
95 | | // copies from parent/sibling chat/shared state objects |
96 | | uint8_t founder_public_sig_key[CRYPTO_PUBLIC_KEY_SIZE]; |
97 | | uint8_t self_public_sig_key[SIG_PUBLIC_KEY_SIZE]; |
98 | | uint8_t self_secret_sig_key[SIG_SECRET_KEY_SIZE]; |
99 | | uint32_t shared_state_version; |
100 | | } Moderation; |
101 | | |
102 | | /** @brief Returns the size in bytes of the packed moderation list. */ |
103 | | uint16_t mod_list_packed_size(const Moderation *_Nonnull moderation); |
104 | | |
105 | | /** @brief Unpacks data into the moderator list. |
106 | | * |
107 | | * @param data should contain num_mods entries of size MOD_LIST_ENTRY_SIZE. |
108 | | * |
109 | | * Returns length of unpacked data on success. |
110 | | * Returns -1 on failure. |
111 | | */ |
112 | | int mod_list_unpack(Moderation *_Nonnull moderation, const uint8_t *_Nonnull data, uint16_t length, uint16_t num_mods); |
113 | | |
114 | | /** @brief Packs moderator list into data. |
115 | | * @param data must have room for the number of bytes returned by `mod_list_packed_size`. |
116 | | */ |
117 | | void mod_list_pack(const Moderation *_Nonnull moderation, uint8_t *_Nonnull data); |
118 | | |
119 | | /** @brief Creates a new moderator list hash and puts it in `hash`. |
120 | | * |
121 | | * @param hash must have room for at least MOD_MODERATION_HASH_SIZE bytes. |
122 | | * |
123 | | * If num_mods is 0 the hash is zeroed. |
124 | | * |
125 | | * Returns true on sucess. |
126 | | */ |
127 | | bool mod_list_make_hash(const Moderation *_Nonnull moderation, uint8_t *_Nonnull hash); |
128 | | |
129 | | /** @brief Puts a sha256 hash of `packed_mod_list` of `length` bytes in `hash`. |
130 | | * |
131 | | * @param hash must have room for at least MOD_MODERATION_HASH_SIZE bytes. |
132 | | */ |
133 | | void mod_list_get_data_hash(uint8_t *_Nonnull hash, const uint8_t *_Nonnull packed_mod_list, uint16_t length); |
134 | | |
135 | | /** @brief Removes moderator at index-th position in the moderator list. |
136 | | * |
137 | | * Returns true on success. |
138 | | */ |
139 | | bool mod_list_remove_index(Moderation *_Nonnull moderation, uint16_t index); |
140 | | |
141 | | /** @brief Removes public_sig_key from the moderator list. |
142 | | * |
143 | | * Returns true on success. |
144 | | */ |
145 | | bool mod_list_remove_entry(Moderation *_Nonnull moderation, const uint8_t *_Nonnull public_sig_key); |
146 | | |
147 | | /** @brief Adds a mod to the moderator list. |
148 | | * |
149 | | * @param mod_data must be MOD_LIST_ENTRY_SIZE bytes. |
150 | | * |
151 | | * Returns true on success. |
152 | | */ |
153 | | bool mod_list_add_entry(Moderation *_Nonnull moderation, const uint8_t *_Nonnull mod_data); |
154 | | |
155 | | /** @return true if the public signature key belongs to a moderator or the founder */ |
156 | | bool mod_list_verify_sig_pk(const Moderation *_Nonnull moderation, const uint8_t *_Nonnull sig_pk); |
157 | | |
158 | | /** @brief Frees all memory associated with the moderator list and sets num_mods to 0. */ |
159 | | void mod_list_cleanup(Moderation *_Nullable moderation); |
160 | | /** @brief Returns the size in bytes of num_sanctions packed sanctions. */ |
161 | | uint16_t sanctions_list_packed_size(uint16_t num_sanctions); |
162 | | |
163 | | /** @brief Packs sanctions into data. Additionally packs the sanctions credentials into creds. |
164 | | * |
165 | | * @param data The byte array being packed. Must have room for the number of bytes returned |
166 | | * by `sanctions_list_packed_size`. |
167 | | * @param length The size of the byte array. |
168 | | * @param sanctions The sanctions list. |
169 | | * @param num_sanctions The number of sanctions in the sanctions list. This value must be the same |
170 | | * value used when calling `sanctions_list_packed_size`. |
171 | | * @param creds The credentials object to fill. |
172 | | * |
173 | | * @retval The length of packed data on success. |
174 | | * @retval -1 on failure. |
175 | | */ |
176 | | int sanctions_list_pack(uint8_t *_Nonnull data, uint16_t length, const Mod_Sanction *_Nullable sanctions, uint16_t num_sanctions, |
177 | | const Mod_Sanction_Creds *_Nullable creds); |
178 | | /** @brief Unpacks sanctions and new sanctions credentials. |
179 | | * |
180 | | * @param sanctions The sanctions array the sanctions data is unpacked into. |
181 | | * @param creds The creds object the creds data is unpacked into. |
182 | | * @param max_sanctions The maximum number of sanctions that the sanctions array can hold. |
183 | | * @param data The packed data array. |
184 | | * @param length The size of the packed data. |
185 | | * @param processed_data_len If non-null, will contain the number of processed bytes on success. |
186 | | * |
187 | | * @retval The number of unpacked entries on success. |
188 | | * @retval -1 on failure. |
189 | | */ |
190 | | int sanctions_list_unpack(Mod_Sanction *_Nonnull sanctions, Mod_Sanction_Creds *_Nonnull creds, uint16_t max_sanctions, |
191 | | const uint8_t *_Nonnull data, uint16_t length, uint16_t *_Nullable processed_data_len); |
192 | | /** @brief Packs sanction list credentials into data. |
193 | | * |
194 | | * @param data must have room for MOD_SANCTIONS_CREDS_SIZE bytes. |
195 | | * |
196 | | * Returns length of packed data. |
197 | | */ |
198 | | uint16_t sanctions_creds_pack(const Mod_Sanction_Creds *_Nonnull creds, uint8_t *_Nonnull data); |
199 | | |
200 | | /** @brief Unpacks sanctions credentials into creds from data. |
201 | | * |
202 | | * @param data must have room for MOD_SANCTIONS_CREDS_SIZE bytes. |
203 | | * |
204 | | * Returns the length of the data processed. |
205 | | */ |
206 | | uint16_t sanctions_creds_unpack(Mod_Sanction_Creds *_Nonnull creds, const uint8_t *_Nonnull data); |
207 | | |
208 | | /** @brief Updates sanction list credentials. |
209 | | * |
210 | | * Increment version, replace sig_pk with your own, update hash to reflect new |
211 | | * sanction list, and sign new hash signature. |
212 | | * |
213 | | * Returns true on success. |
214 | | */ |
215 | | bool sanctions_list_make_creds(Moderation *_Nonnull moderation); |
216 | | |
217 | | /** @brief Validates all sanctions list entries as well as the list itself. |
218 | | * |
219 | | * Returns true if all entries are valid. |
220 | | * Returns false if one or more entries are invalid. |
221 | | */ |
222 | | bool sanctions_list_check_integrity(const Moderation *_Nonnull moderation, const Mod_Sanction_Creds *_Nonnull creds, const Mod_Sanction *_Nonnull sanctions, uint16_t num_sanctions); |
223 | | |
224 | | /** @brief Adds an entry to the sanctions list. |
225 | | * |
226 | | * The entry is first validated and the resulting new sanction list is |
227 | | * compared against the new credentials. |
228 | | * |
229 | | * Entries must be unique. |
230 | | * |
231 | | * Returns true on success. |
232 | | */ |
233 | | bool sanctions_list_add_entry(Moderation *_Nonnull moderation, const Mod_Sanction *_Nonnull sanction, const Mod_Sanction_Creds *_Nullable creds); |
234 | | /** @brief Creates a new sanction entry for `public_key` where type is one of Mod_Sanction_Type. |
235 | | * |
236 | | * New entry is signed and placed in the sanctions list. |
237 | | * |
238 | | * Returns true on success. |
239 | | */ |
240 | | bool sanctions_list_make_entry(Moderation *_Nonnull moderation, const uint8_t *_Nonnull public_key, Mod_Sanction *_Nonnull sanction, uint8_t type); |
241 | | |
242 | | /** @return true if public key is in the observer list. */ |
243 | | bool sanctions_list_is_observer(const Moderation *_Nonnull moderation, const uint8_t *_Nonnull public_key); |
244 | | |
245 | | /** @return true if sanction already exists in the sanctions list. */ |
246 | | bool sanctions_list_entry_exists(const Moderation *_Nonnull moderation, const Mod_Sanction *_Nonnull sanction); |
247 | | |
248 | | /** @brief Removes observer entry for public key from sanction list. |
249 | | * |
250 | | * If creds is NULL we make new credentials (this should only be done by a moderator or founder) |
251 | | * |
252 | | * Returns false on failure or if entry was not found. |
253 | | */ |
254 | | bool sanctions_list_remove_observer(Moderation *_Nonnull moderation, const uint8_t *_Nonnull public_key, |
255 | | const Mod_Sanction_Creds *_Nullable creds); |
256 | | /** @brief Replaces all sanctions list signatures made by public_sig_key with the caller's. |
257 | | * |
258 | | * This is called whenever the founder demotes a moderator. |
259 | | * |
260 | | * Returns the number of entries re-signed. |
261 | | */ |
262 | | uint16_t sanctions_list_replace_sig(Moderation *_Nonnull moderation, const uint8_t *_Nonnull public_sig_key); |
263 | | |
264 | | void sanctions_list_cleanup(Moderation *_Nonnull moderation); |
265 | | |
266 | | #ifdef __cplusplus |
267 | | } /* extern "C" */ |
268 | | #endif |
269 | | |
270 | | #endif /* C_TOXCORE_TOXCORE_GROUP_MODERATION_H */ |