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