/work/toxcore/onion_announce.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* SPDX-License-Identifier: GPL-3.0-or-later |
2 | | * Copyright © 2016-2025 The TokTok team. |
3 | | * Copyright © 2013 Tox project. |
4 | | */ |
5 | | |
6 | | /** |
7 | | * Implementation of the announce part of docs/Prevent_Tracking.txt |
8 | | */ |
9 | | #include "onion_announce.h" |
10 | | |
11 | | #include <assert.h> |
12 | | #include <string.h> |
13 | | |
14 | | #include "DHT.h" |
15 | | #include "LAN_discovery.h" |
16 | | #include "attributes.h" |
17 | | #include "ccompat.h" |
18 | | #include "crypto_core.h" |
19 | | #include "logger.h" |
20 | | #include "mem.h" |
21 | | #include "mono_time.h" |
22 | | #include "network.h" |
23 | | #include "onion.h" |
24 | | #include "shared_key_cache.h" |
25 | | #include "sort.h" |
26 | | #include "timed_auth.h" |
27 | | #include "util.h" |
28 | | |
29 | 118k | #define PING_ID_TIMEOUT ONION_ANNOUNCE_TIMEOUT |
30 | | |
31 | 5.96k | #define ANNOUNCE_REQUEST_MIN_SIZE_RECV (ONION_ANNOUNCE_REQUEST_MIN_SIZE + ONION_RETURN_3) |
32 | 2.98k | #define ANNOUNCE_REQUEST_MAX_SIZE_RECV (ONION_ANNOUNCE_REQUEST_MAX_SIZE + ONION_RETURN_3) |
33 | | |
34 | | /* TODO(Jfreegman): DEPRECATE */ |
35 | 58.2k | #define ANNOUNCE_REQUEST_SIZE_RECV (ONION_ANNOUNCE_REQUEST_SIZE + ONION_RETURN_3) |
36 | | |
37 | 19.8k | #define DATA_REQUEST_MIN_SIZE ONION_DATA_REQUEST_MIN_SIZE |
38 | 3.71k | #define DATA_REQUEST_MIN_SIZE_RECV (DATA_REQUEST_MIN_SIZE + ONION_RETURN_3) |
39 | | |
40 | 2.98k | #define ONION_MINIMAL_SIZE (ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE * 2 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH) |
41 | | |
42 | | /* Settings for the shared key cache */ |
43 | 2.88k | #define MAX_KEYS_PER_SLOT 4 |
44 | 2.88k | #define KEYS_TIMEOUT 600 |
45 | | |
46 | | static_assert(ONION_PING_ID_SIZE == CRYPTO_PUBLIC_KEY_SIZE, |
47 | | "announce response packets assume that ONION_PING_ID_SIZE is equal to CRYPTO_PUBLIC_KEY_SIZE"); |
48 | | |
49 | | typedef struct Onion_Announce_Entry { |
50 | | uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE]; |
51 | | IP_Port ret_ip_port; |
52 | | uint8_t ret[ONION_RETURN_3]; |
53 | | uint8_t data_public_key[CRYPTO_PUBLIC_KEY_SIZE]; |
54 | | uint64_t announce_time; |
55 | | } Onion_Announce_Entry; |
56 | | |
57 | | struct Onion_Announce { |
58 | | const Logger *log; |
59 | | const Mono_Time *mono_time; |
60 | | const Random *rng; |
61 | | const Memory *mem; |
62 | | DHT *dht; |
63 | | Networking_Core *net; |
64 | | Onion_Announce_Entry entries[ONION_ANNOUNCE_MAX_ENTRIES]; |
65 | | uint8_t hmac_key[CRYPTO_HMAC_KEY_SIZE]; |
66 | | |
67 | | Shared_Key_Cache *shared_keys_recv; |
68 | | |
69 | | uint16_t extra_data_max_size; |
70 | | pack_extra_data_cb *extra_data_callback; |
71 | | void *extra_data_object; |
72 | | }; |
73 | | |
74 | | void onion_announce_extra_data_callback(Onion_Announce *onion_a, uint16_t extra_data_max_size, |
75 | | pack_extra_data_cb *extra_data_callback, void *extra_data_object) |
76 | 2.77k | { |
77 | 2.77k | onion_a->extra_data_max_size = extra_data_max_size; |
78 | 2.77k | onion_a->extra_data_callback = extra_data_callback; |
79 | 2.77k | onion_a->extra_data_object = extra_data_object; |
80 | 2.77k | } |
81 | | |
82 | | uint8_t *onion_announce_entry_public_key(Onion_Announce *onion_a, uint32_t entry) |
83 | 3 | { |
84 | 3 | return onion_a->entries[entry].public_key; |
85 | 3 | } |
86 | | |
87 | | void onion_announce_entry_set_time(Onion_Announce *onion_a, uint32_t entry, uint64_t announce_time) |
88 | 1 | { |
89 | 1 | onion_a->entries[entry].announce_time = announce_time; |
90 | 1 | } |
91 | | |
92 | | /** @brief Create an onion announce request packet in packet of max_packet_length. |
93 | | * |
94 | | * Recommended value for max_packet_length is ONION_ANNOUNCE_REQUEST_MIN_SIZE. |
95 | | * |
96 | | * dest_client_id is the public key of the node the packet will be sent to. |
97 | | * public_key and secret_key is the kepair which will be used to encrypt the request. |
98 | | * ping_id is the ping id that will be sent in the request. |
99 | | * client_id is the client id of the node we are searching for. |
100 | | * data_public_key is the public key we want others to encrypt their data packets with. |
101 | | * sendback_data is the data of ONION_ANNOUNCE_SENDBACK_DATA_LENGTH length that we expect to |
102 | | * receive back in the response. |
103 | | * |
104 | | * return -1 on failure. |
105 | | * return packet length on success. |
106 | | */ |
107 | | int create_announce_request(const Memory *mem, const Random *rng, uint8_t *packet, uint16_t max_packet_length, const uint8_t *dest_client_id, |
108 | | const uint8_t *public_key, const uint8_t *secret_key, const uint8_t *ping_id, const uint8_t *client_id, |
109 | | const uint8_t *data_public_key, uint64_t sendback_data) |
110 | 67.4k | { |
111 | 67.4k | if (max_packet_length < ONION_ANNOUNCE_REQUEST_MIN_SIZE) { |
112 | 0 | return -1; |
113 | 0 | } |
114 | | |
115 | 67.4k | uint8_t plain[ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_PUBLIC_KEY_SIZE + |
116 | 67.4k | ONION_ANNOUNCE_SENDBACK_DATA_LENGTH]; |
117 | 67.4k | memcpy(plain, ping_id, ONION_PING_ID_SIZE); |
118 | 67.4k | memcpy(plain + ONION_PING_ID_SIZE, client_id, CRYPTO_PUBLIC_KEY_SIZE); |
119 | 67.4k | memcpy(plain + ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE, data_public_key, CRYPTO_PUBLIC_KEY_SIZE); |
120 | 67.4k | memcpy(plain + ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_PUBLIC_KEY_SIZE, &sendback_data, |
121 | 67.4k | sizeof(sendback_data)); |
122 | | |
123 | 67.4k | packet[0] = NET_PACKET_ANNOUNCE_REQUEST_OLD; |
124 | 67.4k | random_nonce(rng, packet + 1); |
125 | | |
126 | 67.4k | const int len = encrypt_data(mem, dest_client_id, secret_key, packet + 1, plain, sizeof(plain), |
127 | 67.4k | packet + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE); |
128 | | |
129 | 67.4k | if ((uint32_t)len + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE != ONION_ANNOUNCE_REQUEST_MIN_SIZE) { |
130 | 38 | return -1; |
131 | 38 | } |
132 | | |
133 | 67.4k | memcpy(packet + 1 + CRYPTO_NONCE_SIZE, public_key, CRYPTO_PUBLIC_KEY_SIZE); |
134 | | |
135 | 67.4k | return ONION_ANNOUNCE_REQUEST_MIN_SIZE; |
136 | 67.4k | } |
137 | | |
138 | | /** @brief Create an onion data request packet in packet of max_packet_length. |
139 | | * |
140 | | * Recommended value for max_packet_length is ONION_ANNOUNCE_REQUEST_SIZE. |
141 | | * |
142 | | * public_key is the real public key of the node which we want to send the data of length length to. |
143 | | * encrypt_public_key is the public key used to encrypt the data packet. |
144 | | * |
145 | | * nonce is the nonce to encrypt this packet with |
146 | | * |
147 | | * return -1 on failure. |
148 | | * return 0 on success. |
149 | | */ |
150 | | int create_data_request(const Memory *mem, const Random *rng, uint8_t *packet, uint16_t max_packet_length, const uint8_t *public_key, |
151 | | const uint8_t *encrypt_public_key, const uint8_t *nonce, const uint8_t *data, uint16_t length) |
152 | 4.04k | { |
153 | 4.04k | if (DATA_REQUEST_MIN_SIZE + length > max_packet_length) { |
154 | 0 | return -1; |
155 | 0 | } |
156 | | |
157 | 4.04k | if (DATA_REQUEST_MIN_SIZE + length > ONION_MAX_DATA_SIZE) { |
158 | 0 | return -1; |
159 | 0 | } |
160 | | |
161 | 4.04k | packet[0] = NET_PACKET_ONION_DATA_REQUEST; |
162 | 4.04k | memcpy(packet + 1, public_key, CRYPTO_PUBLIC_KEY_SIZE); |
163 | 4.04k | memcpy(packet + 1 + CRYPTO_PUBLIC_KEY_SIZE, nonce, CRYPTO_NONCE_SIZE); |
164 | | |
165 | 4.04k | uint8_t random_public_key[CRYPTO_PUBLIC_KEY_SIZE]; |
166 | 4.04k | uint8_t random_secret_key[CRYPTO_SECRET_KEY_SIZE]; |
167 | 4.04k | crypto_new_keypair(rng, random_public_key, random_secret_key); |
168 | | |
169 | 4.04k | memcpy(packet + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE, random_public_key, CRYPTO_PUBLIC_KEY_SIZE); |
170 | | |
171 | 4.04k | const int len = encrypt_data(mem, encrypt_public_key, random_secret_key, packet + 1 + CRYPTO_PUBLIC_KEY_SIZE, data, length, |
172 | 4.04k | packet + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE); |
173 | | |
174 | 4.04k | if (1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + len != DATA_REQUEST_MIN_SIZE + |
175 | 4.04k | length) { |
176 | 0 | return -1; |
177 | 0 | } |
178 | | |
179 | 4.04k | return DATA_REQUEST_MIN_SIZE + length; |
180 | 4.04k | } |
181 | | |
182 | | /** @brief Create and send an onion announce request packet. |
183 | | * |
184 | | * path is the path the request will take before it is sent to dest. |
185 | | * |
186 | | * public_key and secret_key is the kepair which will be used to encrypt the request. |
187 | | * ping_id is the ping id that will be sent in the request. |
188 | | * client_id is the client id of the node we are searching for. |
189 | | * data_public_key is the public key we want others to encrypt their data packets with. |
190 | | * sendback_data is the data of ONION_ANNOUNCE_SENDBACK_DATA_LENGTH length that we expect to |
191 | | * receive back in the response. |
192 | | * |
193 | | * return -1 on failure. |
194 | | * return 0 on success. |
195 | | */ |
196 | | int send_announce_request( |
197 | | const Logger *log, const Memory *mem, const Networking_Core *net, const Random *rng, |
198 | | const Onion_Path *path, const Node_format *dest, |
199 | | const uint8_t *public_key, const uint8_t *secret_key, |
200 | | const uint8_t *ping_id, const uint8_t *client_id, |
201 | | const uint8_t *data_public_key, uint64_t sendback_data) |
202 | 2 | { |
203 | 2 | uint8_t request[ONION_ANNOUNCE_REQUEST_MIN_SIZE]; |
204 | 2 | int len = create_announce_request(mem, rng, request, sizeof(request), dest->public_key, public_key, secret_key, ping_id, |
205 | 2 | client_id, data_public_key, sendback_data); |
206 | | |
207 | 2 | if (len != sizeof(request)) { |
208 | 0 | return -1; |
209 | 0 | } |
210 | | |
211 | 2 | uint8_t packet[ONION_MAX_PACKET_SIZE]; |
212 | 2 | len = create_onion_packet(mem, rng, packet, sizeof(packet), path, &dest->ip_port, request, sizeof(request)); |
213 | | |
214 | 2 | if (len == -1) { |
215 | 0 | return -1; |
216 | 0 | } |
217 | | |
218 | 2 | if (sendpacket(net, &path->ip_port1, packet, len) != len) { |
219 | 0 | return -1; |
220 | 0 | } |
221 | | |
222 | 2 | return 0; |
223 | 2 | } |
224 | | |
225 | | /** @brief Create and send an onion data request packet. |
226 | | * |
227 | | * path is the path the request will take before it is sent to dest. |
228 | | * (if dest knows the person with the public_key they should |
229 | | * send the packet to that person in the form of a response) |
230 | | * |
231 | | * public_key is the real public key of the node which we want to send the data of length length to. |
232 | | * encrypt_public_key is the public key used to encrypt the data packet. |
233 | | * |
234 | | * nonce is the nonce to encrypt this packet with |
235 | | * |
236 | | * The maximum length of data is MAX_DATA_REQUEST_SIZE. |
237 | | * |
238 | | * return -1 on failure. |
239 | | * return 0 on success. |
240 | | */ |
241 | | int send_data_request( |
242 | | const Logger *log, const Memory *mem, const Networking_Core *net, const Random *rng, const Onion_Path *path, const IP_Port *dest, |
243 | | const uint8_t *public_key, const uint8_t *encrypt_public_key, const uint8_t *nonce, |
244 | | const uint8_t *data, uint16_t length) |
245 | 1 | { |
246 | 1 | uint8_t request[ONION_MAX_DATA_SIZE]; |
247 | 1 | int len = create_data_request(mem, rng, request, sizeof(request), public_key, encrypt_public_key, nonce, data, length); |
248 | | |
249 | 1 | if (len == -1) { |
250 | 0 | return -1; |
251 | 0 | } |
252 | | |
253 | 1 | uint8_t packet[ONION_MAX_PACKET_SIZE]; |
254 | 1 | len = create_onion_packet(mem, rng, packet, sizeof(packet), path, dest, request, len); |
255 | | |
256 | 1 | if (len == -1) { |
257 | 0 | return -1; |
258 | 0 | } |
259 | | |
260 | 1 | if (sendpacket(net, &path->ip_port1, packet, len) != len) { |
261 | 0 | return -1; |
262 | 0 | } |
263 | | |
264 | 1 | return 0; |
265 | 1 | } |
266 | | |
267 | | /** @brief check if public key is in entries list |
268 | | * |
269 | | * return -1 if no |
270 | | * return position in list if yes |
271 | | */ |
272 | | static int in_entries(const Onion_Announce *_Nonnull onion_a, const uint8_t *_Nonnull public_key) |
273 | 70.1k | { |
274 | 11.0M | for (unsigned int i = 0; i < ONION_ANNOUNCE_MAX_ENTRIES; ++i) { |
275 | 11.0M | if (!mono_time_is_timeout(onion_a->mono_time, onion_a->entries[i].announce_time, ONION_ANNOUNCE_TIMEOUT) |
276 | 11.0M | && pk_equal(onion_a->entries[i].public_key, public_key)) { |
277 | 53.3k | return i; |
278 | 53.3k | } |
279 | 11.0M | } |
280 | | |
281 | 16.7k | return -1; |
282 | 70.1k | } |
283 | | |
284 | | typedef struct Onion_Announce_Entry_Cmp { |
285 | | const Memory *mem; |
286 | | const Mono_Time *mono_time; |
287 | | const uint8_t *comp_public_key; |
288 | | } Onion_Announce_Entry_Cmp; |
289 | | |
290 | | static int onion_announce_entry_cmp(const Onion_Announce_Entry_Cmp *_Nonnull cmp, const Onion_Announce_Entry *_Nonnull entry1, const Onion_Announce_Entry *_Nonnull entry2) |
291 | 4.85M | { |
292 | 4.85M | const bool t1 = mono_time_is_timeout(cmp->mono_time, entry1->announce_time, ONION_ANNOUNCE_TIMEOUT); |
293 | 4.85M | const bool t2 = mono_time_is_timeout(cmp->mono_time, entry2->announce_time, ONION_ANNOUNCE_TIMEOUT); |
294 | | |
295 | 4.85M | if (t1 && t2) { |
296 | 4.70M | return 0; |
297 | 4.70M | } |
298 | | |
299 | 149k | if (t1) { |
300 | 157 | return -1; |
301 | 157 | } |
302 | | |
303 | 149k | if (t2) { |
304 | 42.3k | return 1; |
305 | 42.3k | } |
306 | | |
307 | 106k | const int closest = id_closest(cmp->comp_public_key, entry1->public_key, entry2->public_key); |
308 | | |
309 | 106k | if (closest == 1) { |
310 | 95.6k | return 1; |
311 | 95.6k | } |
312 | | |
313 | 11.3k | if (closest == 2) { |
314 | 11.3k | return -1; |
315 | 11.3k | } |
316 | | |
317 | 0 | return 0; |
318 | 11.3k | } |
319 | | |
320 | | static bool onion_announce_entry_less_handler(const void *_Nonnull object, const void *_Nonnull a, const void *_Nonnull b) |
321 | 4.85M | { |
322 | 4.85M | const Onion_Announce_Entry_Cmp *cmp = (const Onion_Announce_Entry_Cmp *)object; |
323 | 4.85M | const Onion_Announce_Entry *entry1 = (const Onion_Announce_Entry *)a; |
324 | 4.85M | const Onion_Announce_Entry *entry2 = (const Onion_Announce_Entry *)b; |
325 | | |
326 | 4.85M | return onion_announce_entry_cmp(cmp, entry1, entry2) < 0; |
327 | 4.85M | } |
328 | | |
329 | | static const void *onion_announce_entry_get_handler(const void *_Nonnull arr, uint32_t index) |
330 | 23.7M | { |
331 | 23.7M | const Onion_Announce_Entry *entries = (const Onion_Announce_Entry *)arr; |
332 | 23.7M | return &entries[index]; |
333 | 23.7M | } |
334 | | |
335 | | static void onion_announce_entry_set_handler(void *_Nonnull arr, uint32_t index, const void *_Nonnull val) |
336 | 18.9M | { |
337 | 18.9M | Onion_Announce_Entry *entries = (Onion_Announce_Entry *)arr; |
338 | 18.9M | const Onion_Announce_Entry *entry = (const Onion_Announce_Entry *)val; |
339 | 18.9M | entries[index] = *entry; |
340 | 18.9M | } |
341 | | |
342 | | static void *onion_announce_entry_subarr_handler(void *_Nonnull arr, uint32_t index, uint32_t size) |
343 | 2.37M | { |
344 | 2.37M | Onion_Announce_Entry *entries = (Onion_Announce_Entry *)arr; |
345 | 2.37M | return &entries[index]; |
346 | 2.37M | } |
347 | | |
348 | | static void *onion_announce_entry_alloc_handler(const void *_Nonnull object, uint32_t size) |
349 | 7.39k | { |
350 | 7.39k | const Onion_Announce_Entry_Cmp *cmp = (const Onion_Announce_Entry_Cmp *)object; |
351 | 7.39k | Onion_Announce_Entry *tmp = (Onion_Announce_Entry *)mem_valloc(cmp->mem, size, sizeof(Onion_Announce_Entry)); |
352 | | |
353 | 7.39k | if (tmp == nullptr) { |
354 | 8 | return nullptr; |
355 | 8 | } |
356 | | |
357 | 7.38k | return tmp; |
358 | 7.39k | } |
359 | | |
360 | | static void onion_announce_entry_delete_handler(const void *_Nonnull object, void *_Nonnull arr, uint32_t size) |
361 | 7.38k | { |
362 | 7.38k | const Onion_Announce_Entry_Cmp *cmp = (const Onion_Announce_Entry_Cmp *)object; |
363 | 7.38k | mem_delete(cmp->mem, arr); |
364 | 7.38k | } |
365 | | |
366 | | static const Sort_Funcs onion_announce_entry_cmp_funcs = { |
367 | | onion_announce_entry_less_handler, |
368 | | onion_announce_entry_get_handler, |
369 | | onion_announce_entry_set_handler, |
370 | | onion_announce_entry_subarr_handler, |
371 | | onion_announce_entry_alloc_handler, |
372 | | onion_announce_entry_delete_handler, |
373 | | }; |
374 | | |
375 | | static void sort_onion_announce_list(const Memory *_Nonnull mem, const Mono_Time *_Nonnull mono_time, Onion_Announce_Entry *_Nonnull list, unsigned int length, |
376 | | const uint8_t *_Nonnull comp_public_key) |
377 | 7.39k | { |
378 | | // Pass comp_public_key to sort with each Onion_Announce_Entry entry, so the |
379 | | // comparison function can use it as the base of comparison. |
380 | 7.39k | const Onion_Announce_Entry_Cmp cmp = { |
381 | 7.39k | mem, |
382 | 7.39k | mono_time, |
383 | 7.39k | comp_public_key, |
384 | 7.39k | }; |
385 | | |
386 | 7.39k | merge_sort(list, length, &cmp, &onion_announce_entry_cmp_funcs); |
387 | 7.39k | } |
388 | | |
389 | | /** @brief add entry to entries list |
390 | | * |
391 | | * return -1 if failure |
392 | | * return position if added |
393 | | */ |
394 | | static int add_to_entries(Onion_Announce *_Nonnull onion_a, const IP_Port *_Nonnull ret_ip_port, const uint8_t *_Nonnull public_key, const uint8_t *_Nonnull data_public_key, |
395 | | const uint8_t *_Nonnull ret) |
396 | 7.39k | { |
397 | 7.39k | int pos = in_entries(onion_a, public_key); |
398 | | |
399 | 7.39k | if (pos == -1) { |
400 | 683k | for (unsigned i = 0; i < ONION_ANNOUNCE_MAX_ENTRIES; ++i) { |
401 | 679k | if (mono_time_is_timeout(onion_a->mono_time, onion_a->entries[i].announce_time, ONION_ANNOUNCE_TIMEOUT)) { |
402 | 656k | pos = i; |
403 | 656k | } |
404 | 679k | } |
405 | 4.24k | } |
406 | | |
407 | 7.39k | if (pos == -1) { |
408 | 0 | if (id_closest(dht_get_self_public_key(onion_a->dht), public_key, onion_a->entries[0].public_key) == 1) { |
409 | 0 | pos = 0; |
410 | 0 | } |
411 | 0 | } |
412 | | |
413 | 7.39k | if (pos == -1) { |
414 | 0 | return -1; |
415 | 0 | } |
416 | | |
417 | 7.39k | memcpy(onion_a->entries[pos].public_key, public_key, CRYPTO_PUBLIC_KEY_SIZE); |
418 | 7.39k | onion_a->entries[pos].ret_ip_port = *ret_ip_port; |
419 | 7.39k | memcpy(onion_a->entries[pos].ret, ret, ONION_RETURN_3); |
420 | 7.39k | memcpy(onion_a->entries[pos].data_public_key, data_public_key, CRYPTO_PUBLIC_KEY_SIZE); |
421 | 7.39k | onion_a->entries[pos].announce_time = mono_time_get(onion_a->mono_time); |
422 | | |
423 | 7.39k | sort_onion_announce_list(onion_a->mem, onion_a->mono_time, |
424 | 7.39k | onion_a->entries, ONION_ANNOUNCE_MAX_ENTRIES, |
425 | 7.39k | dht_get_self_public_key(onion_a->dht)); |
426 | 7.39k | return in_entries(onion_a, public_key); |
427 | 7.39k | } |
428 | | |
429 | | static void make_announce_payload_helper(const Onion_Announce *_Nonnull onion_a, const uint8_t *_Nonnull ping_id, uint8_t *_Nonnull response, int index, |
430 | | const uint8_t *_Nonnull packet_public_key, const uint8_t *_Nonnull data_public_key) |
431 | 59.0k | { |
432 | 59.0k | if (index < 0) { |
433 | 12.5k | response[0] = 0; |
434 | 12.5k | memcpy(response + 1, ping_id, ONION_PING_ID_SIZE); |
435 | 12.5k | return; |
436 | 12.5k | } |
437 | | |
438 | 46.4k | if (pk_equal(onion_a->entries[index].public_key, packet_public_key)) { |
439 | 31.1k | if (!pk_equal(onion_a->entries[index].data_public_key, data_public_key)) { |
440 | 142 | response[0] = 0; |
441 | 142 | memcpy(response + 1, ping_id, ONION_PING_ID_SIZE); |
442 | 31.0k | } else { |
443 | 31.0k | response[0] = 2; |
444 | 31.0k | memcpy(response + 1, ping_id, ONION_PING_ID_SIZE); |
445 | 31.0k | } |
446 | 31.1k | } else { |
447 | 15.3k | response[0] = 1; |
448 | 15.3k | memcpy(response + 1, onion_a->entries[index].data_public_key, CRYPTO_PUBLIC_KEY_SIZE); |
449 | 15.3k | } |
450 | 46.4k | } |
451 | | |
452 | | /** @brief Handle an onion announce request, possibly with extra data for group chats. |
453 | | * |
454 | | * @param onion_a The announce object. |
455 | | * @param source Requester IP/Port. |
456 | | * @param packet Encrypted incoming packet. |
457 | | * @param length Length of incoming packet. |
458 | | * @param response_packet_id Packet ID to use for the onion announce response. |
459 | | * @param plain_size Expected size of the decrypted packet. This function returns an error if the |
460 | | * actual decrypted size is not exactly equal to this number. |
461 | | * @param want_node_count If true, the packed nodes in the response are preceded by the number of |
462 | | * nodes sent in the packet. This is necessary if you want to send extra data after the nodes. |
463 | | * @param max_extra_size Amount of memory to allocate in the outgoing packet to be filled by the |
464 | | * extra data callback. |
465 | | * @param pack_extra_data_callback Callback that may write extra data into the packet. |
466 | | * |
467 | | * @retval 1 on failure. |
468 | | * @retval 0 on success. |
469 | | */ |
470 | | static int handle_announce_request_common( |
471 | | Onion_Announce *_Nonnull onion_a, const IP_Port *_Nonnull source, const uint8_t *_Nonnull packet, uint16_t length, |
472 | | uint8_t response_packet_id, uint16_t plain_size, bool want_node_count, uint16_t max_extra_size, |
473 | | pack_extra_data_cb *_Nullable pack_extra_data_callback) |
474 | 59.7k | { |
475 | 59.7k | const uint8_t *packet_public_key = packet + 1 + CRYPTO_NONCE_SIZE; |
476 | 59.7k | const uint8_t *shared_key = shared_key_cache_lookup(onion_a->shared_keys_recv, packet_public_key); |
477 | | |
478 | 59.7k | if (shared_key == nullptr) { |
479 | | /* Error looking up/deriving the shared key */ |
480 | 0 | return 1; |
481 | 0 | } |
482 | | |
483 | 59.7k | uint8_t *plain = (uint8_t *)mem_balloc(onion_a->mem, plain_size); |
484 | | |
485 | 59.7k | if (plain == nullptr) { |
486 | 15 | return 1; |
487 | 15 | } |
488 | | |
489 | 59.6k | const int decrypted_len = decrypt_data_symmetric(onion_a->mem, shared_key, packet + 1, |
490 | 59.6k | packet + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE, plain_size + CRYPTO_MAC_SIZE, plain); |
491 | | |
492 | 59.6k | if ((uint32_t)decrypted_len != plain_size) { |
493 | 651 | mem_delete(onion_a->mem, plain); |
494 | 651 | return 1; |
495 | 651 | } |
496 | | |
497 | 59.0k | const uint16_t ping_id_data_len = CRYPTO_PUBLIC_KEY_SIZE + SIZE_IPPORT; |
498 | 59.0k | uint8_t ping_id_data[CRYPTO_PUBLIC_KEY_SIZE + SIZE_IPPORT]; |
499 | 59.0k | memcpy(ping_id_data, packet_public_key, CRYPTO_PUBLIC_KEY_SIZE); |
500 | 59.0k | const int packed_len = pack_ip_port(onion_a->log, &ping_id_data[CRYPTO_PUBLIC_KEY_SIZE], SIZE_IPPORT, source); |
501 | 59.0k | if (packed_len < 0) { |
502 | 0 | LOGGER_ERROR(onion_a->log, "failed to pack IP/Port"); |
503 | 0 | mem_delete(onion_a->mem, plain); |
504 | 0 | return 1; |
505 | 0 | } |
506 | 59.0k | assert(packed_len <= SIZE_IPPORT); |
507 | 59.0k | memzero(&ping_id_data[CRYPTO_PUBLIC_KEY_SIZE + packed_len], SIZE_IPPORT - packed_len); |
508 | 59.0k | const uint8_t *data_public_key = plain + ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE; |
509 | | |
510 | 59.0k | int index; |
511 | | |
512 | 59.0k | if (check_timed_auth(onion_a->mono_time, PING_ID_TIMEOUT, onion_a->hmac_key, |
513 | 59.0k | ping_id_data, ping_id_data_len, plain)) { |
514 | 7.39k | index = add_to_entries(onion_a, source, packet_public_key, data_public_key, |
515 | 7.39k | packet + (length - ONION_RETURN_3)); |
516 | 51.6k | } else { |
517 | 51.6k | index = in_entries(onion_a, plain + ONION_PING_ID_SIZE); |
518 | 51.6k | } |
519 | | |
520 | | /* Respond with a announce response packet */ |
521 | 59.0k | Node_format nodes_list[MAX_SENT_NODES]; |
522 | 59.0k | const unsigned int num_nodes = |
523 | 59.0k | get_close_nodes(onion_a->dht, plain + ONION_PING_ID_SIZE, nodes_list, net_family_unspec(), ip_is_lan(&source->ip), false); |
524 | | |
525 | 59.0k | assert(num_nodes <= UINT8_MAX); |
526 | | |
527 | 59.0k | uint8_t nonce[CRYPTO_NONCE_SIZE]; |
528 | 59.0k | random_nonce(onion_a->rng, nonce); |
529 | | |
530 | 59.0k | const uint16_t nodes_offset = 1 + ONION_PING_ID_SIZE + (want_node_count ? 1 : 0); |
531 | 59.0k | const uint16_t response_size = nodes_offset |
532 | 59.0k | + MAX_SENT_NODES * PACKED_NODE_SIZE_IP6 |
533 | 59.0k | + max_extra_size; |
534 | 59.0k | uint8_t *response = (uint8_t *)mem_balloc(onion_a->mem, response_size); |
535 | | |
536 | 59.0k | if (response == nullptr) { |
537 | 12 | mem_delete(onion_a->mem, plain); |
538 | 12 | return 1; |
539 | 12 | } |
540 | | |
541 | 59.0k | uint8_t ping_id[TIMED_AUTH_SIZE]; |
542 | 59.0k | generate_timed_auth(onion_a->mono_time, PING_ID_TIMEOUT, onion_a->hmac_key, |
543 | 59.0k | ping_id_data, ping_id_data_len, ping_id); |
544 | | |
545 | 59.0k | make_announce_payload_helper(onion_a, ping_id, response, index, packet_public_key, data_public_key); |
546 | | |
547 | 59.0k | int nodes_length = 0; |
548 | | |
549 | 59.0k | if (num_nodes != 0) { |
550 | 58.3k | nodes_length = pack_nodes(onion_a->log, &response[nodes_offset], num_nodes * PACKED_NODE_SIZE_IP6, nodes_list, |
551 | 58.3k | (uint16_t)num_nodes); |
552 | | |
553 | 58.3k | if (nodes_length <= 0) { |
554 | 0 | LOGGER_WARNING(onion_a->log, "Failed to pack nodes"); |
555 | 0 | mem_delete(onion_a->mem, response); |
556 | 0 | mem_delete(onion_a->mem, plain); |
557 | 0 | return 1; |
558 | 0 | } |
559 | 58.3k | } |
560 | | |
561 | 59.0k | uint16_t offset = nodes_offset + nodes_length; |
562 | | |
563 | 59.0k | if (want_node_count) { |
564 | 1.49k | response[1 + ONION_PING_ID_SIZE] = (uint8_t)num_nodes; |
565 | 1.49k | } |
566 | | |
567 | 59.0k | const int extra_size = pack_extra_data_callback == nullptr ? 0 |
568 | 59.0k | : pack_extra_data_callback(onion_a->extra_data_object, |
569 | 1.49k | onion_a->log, onion_a->mem, onion_a->mono_time, num_nodes, |
570 | 1.49k | plain + ONION_MINIMAL_SIZE, length - ANNOUNCE_REQUEST_MIN_SIZE_RECV, |
571 | 1.49k | response, response_size, offset); |
572 | | |
573 | 59.0k | if (extra_size == -1) { |
574 | 0 | mem_delete(onion_a->mem, response); |
575 | 0 | mem_delete(onion_a->mem, plain); |
576 | 0 | return 1; |
577 | 0 | } |
578 | | |
579 | 59.0k | offset += extra_size; |
580 | | |
581 | 59.0k | uint8_t data[ONION_ANNOUNCE_RESPONSE_MAX_SIZE]; |
582 | 59.0k | const int len = encrypt_data_symmetric(onion_a->mem, shared_key, nonce, response, offset, |
583 | 59.0k | data + 1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_NONCE_SIZE); |
584 | | |
585 | 59.0k | if (len != offset + CRYPTO_MAC_SIZE) { |
586 | 12 | LOGGER_ERROR(onion_a->log, "Failed to encrypt announce response"); |
587 | 12 | mem_delete(onion_a->mem, response); |
588 | 12 | mem_delete(onion_a->mem, plain); |
589 | 12 | return 1; |
590 | 12 | } |
591 | | |
592 | 59.0k | data[0] = response_packet_id; |
593 | 59.0k | memcpy(data + 1, plain + ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_PUBLIC_KEY_SIZE, |
594 | 59.0k | ONION_ANNOUNCE_SENDBACK_DATA_LENGTH); |
595 | 59.0k | memcpy(data + 1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH, nonce, CRYPTO_NONCE_SIZE); |
596 | | |
597 | 59.0k | if (send_onion_response(onion_a->log, onion_a->net, source, data, |
598 | 59.0k | 1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_NONCE_SIZE + len, |
599 | 59.0k | packet + (length - ONION_RETURN_3)) == -1) { |
600 | 11 | mem_delete(onion_a->mem, response); |
601 | 11 | mem_delete(onion_a->mem, plain); |
602 | 11 | return 1; |
603 | 11 | } |
604 | | |
605 | 59.0k | mem_delete(onion_a->mem, response); |
606 | 59.0k | mem_delete(onion_a->mem, plain); |
607 | 59.0k | return 0; |
608 | 59.0k | } |
609 | | |
610 | | static int handle_gca_announce_request(Onion_Announce *_Nonnull onion_a, const IP_Port *_Nonnull source, const uint8_t *_Nonnull packet, uint16_t length) |
611 | 1.49k | { |
612 | 1.49k | if (length > ANNOUNCE_REQUEST_MAX_SIZE_RECV || length <= ANNOUNCE_REQUEST_MIN_SIZE_RECV) { |
613 | 0 | return 1; |
614 | 0 | } |
615 | | |
616 | 1.49k | if (onion_a->extra_data_callback == nullptr) { |
617 | 0 | return 1; |
618 | 0 | } |
619 | | |
620 | 1.49k | return handle_announce_request_common(onion_a, source, packet, length, NET_PACKET_ANNOUNCE_RESPONSE, |
621 | 1.49k | ONION_MINIMAL_SIZE + length - ANNOUNCE_REQUEST_MIN_SIZE_RECV, |
622 | 1.49k | true, onion_a->extra_data_max_size, onion_a->extra_data_callback); |
623 | 1.49k | } |
624 | | |
625 | | static int handle_announce_request(void *_Nonnull object, const IP_Port *_Nonnull source, const uint8_t *_Nonnull packet, uint16_t length, |
626 | | void *_Nullable userdata) |
627 | 1.49k | { |
628 | 1.49k | Onion_Announce *onion_a = (Onion_Announce *)object; |
629 | 1.49k | if (length != ANNOUNCE_REQUEST_MIN_SIZE_RECV) { |
630 | 1.49k | return handle_gca_announce_request(onion_a, source, packet, length); |
631 | 1.49k | } |
632 | | |
633 | 0 | return handle_announce_request_common(onion_a, source, packet, length, NET_PACKET_ANNOUNCE_RESPONSE, |
634 | 0 | ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE * 2 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH, |
635 | 0 | true, 0, nullptr); |
636 | 1.49k | } |
637 | | |
638 | | /* TODO(Jfreegman): DEPRECATE */ |
639 | | static int handle_announce_request_old(void *_Nonnull object, const IP_Port *_Nonnull source, const uint8_t *_Nonnull packet, uint16_t length, |
640 | | void *_Nullable userdata) |
641 | 58.2k | { |
642 | 58.2k | Onion_Announce *onion_a = (Onion_Announce *)object; |
643 | 58.2k | if (length != ANNOUNCE_REQUEST_SIZE_RECV) { |
644 | 0 | return 1; |
645 | 0 | } |
646 | | |
647 | 58.2k | return handle_announce_request_common(onion_a, source, packet, length, NET_PACKET_ANNOUNCE_RESPONSE_OLD, |
648 | 58.2k | ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE * 2 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH, |
649 | 58.2k | false, 0, nullptr); |
650 | 58.2k | } |
651 | | |
652 | | static int handle_data_request(void *_Nonnull object, const IP_Port *_Nonnull source, const uint8_t *_Nonnull packet, uint16_t length, void *_Nonnull userdata) |
653 | 3.71k | { |
654 | 3.71k | const Onion_Announce *onion_a = (const Onion_Announce *)object; |
655 | | |
656 | 3.71k | if (length <= DATA_REQUEST_MIN_SIZE_RECV) { |
657 | 0 | return 1; |
658 | 0 | } |
659 | | |
660 | 3.71k | if (length > ONION_MAX_PACKET_SIZE) { |
661 | 0 | return 1; |
662 | 0 | } |
663 | | |
664 | 3.71k | const int index = in_entries(onion_a, packet + 1); |
665 | | |
666 | 3.71k | if (index == -1) { |
667 | 9 | return 1; |
668 | 9 | } |
669 | | |
670 | 3.70k | const uint16_t data_size = length - (CRYPTO_PUBLIC_KEY_SIZE + ONION_RETURN_3); |
671 | 3.70k | VLA(uint8_t, data, data_size); |
672 | 3.70k | data[0] = NET_PACKET_ONION_DATA_RESPONSE; |
673 | 3.70k | memcpy(data + 1, packet + 1 + CRYPTO_PUBLIC_KEY_SIZE, length - (1 + CRYPTO_PUBLIC_KEY_SIZE + ONION_RETURN_3)); |
674 | | |
675 | 3.70k | if (send_onion_response(onion_a->log, onion_a->net, &onion_a->entries[index].ret_ip_port, data, data_size, |
676 | 3.70k | onion_a->entries[index].ret) == -1) { |
677 | 0 | return 1; |
678 | 0 | } |
679 | | |
680 | 3.70k | return 0; |
681 | 3.70k | } |
682 | | |
683 | | Onion_Announce *new_onion_announce(const Logger *log, const Memory *mem, const Random *rng, const Mono_Time *mono_time, DHT *dht) |
684 | 2.89k | { |
685 | 2.89k | if (dht == nullptr) { |
686 | 0 | return nullptr; |
687 | 0 | } |
688 | | |
689 | 2.89k | Onion_Announce *onion_a = (Onion_Announce *)mem_alloc(mem, sizeof(Onion_Announce)); |
690 | | |
691 | 2.89k | if (onion_a == nullptr) { |
692 | 17 | return nullptr; |
693 | 17 | } |
694 | | |
695 | 2.88k | onion_a->log = log; |
696 | 2.88k | onion_a->rng = rng; |
697 | 2.88k | onion_a->mem = mem; |
698 | 2.88k | onion_a->mono_time = mono_time; |
699 | 2.88k | onion_a->dht = dht; |
700 | 2.88k | onion_a->net = dht_get_net(dht); |
701 | 2.88k | onion_a->extra_data_max_size = 0; |
702 | 2.88k | onion_a->extra_data_callback = nullptr; |
703 | 2.88k | onion_a->extra_data_object = nullptr; |
704 | 2.88k | new_hmac_key(rng, onion_a->hmac_key); |
705 | | |
706 | 2.88k | onion_a->shared_keys_recv = shared_key_cache_new(log, mono_time, mem, dht_get_self_secret_key(dht), KEYS_TIMEOUT, MAX_KEYS_PER_SLOT); |
707 | 2.88k | if (onion_a->shared_keys_recv == nullptr) { |
708 | | // cppcheck-suppress mismatchAllocDealloc |
709 | 34 | kill_onion_announce(onion_a); |
710 | 34 | return nullptr; |
711 | 34 | } |
712 | | |
713 | 2.84k | networking_registerhandler(onion_a->net, NET_PACKET_ANNOUNCE_REQUEST, &handle_announce_request, onion_a); |
714 | 2.84k | networking_registerhandler(onion_a->net, NET_PACKET_ANNOUNCE_REQUEST_OLD, &handle_announce_request_old, onion_a); |
715 | 2.84k | networking_registerhandler(onion_a->net, NET_PACKET_ONION_DATA_REQUEST, &handle_data_request, onion_a); |
716 | | |
717 | 2.84k | return onion_a; |
718 | 2.88k | } |
719 | | |
720 | | void kill_onion_announce(Onion_Announce *onion_a) |
721 | 2.05k | { |
722 | 2.05k | if (onion_a == nullptr) { |
723 | 51 | return; |
724 | 51 | } |
725 | | |
726 | 2.00k | networking_registerhandler(onion_a->net, NET_PACKET_ANNOUNCE_REQUEST, nullptr, nullptr); |
727 | 2.00k | networking_registerhandler(onion_a->net, NET_PACKET_ANNOUNCE_REQUEST_OLD, nullptr, nullptr); |
728 | 2.00k | networking_registerhandler(onion_a->net, NET_PACKET_ONION_DATA_REQUEST, nullptr, nullptr); |
729 | | |
730 | 2.00k | crypto_memzero(onion_a->hmac_key, CRYPTO_HMAC_KEY_SIZE); |
731 | 2.00k | shared_key_cache_free(onion_a->shared_keys_recv); |
732 | | |
733 | 2.00k | mem_delete(onion_a->mem, onion_a); |
734 | 2.00k | } |