Coverage Report

Created: 2025-10-08 19:34

/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 */