/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-2026 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 | 68.6k | #define PING_ID_TIMEOUT ONION_ANNOUNCE_TIMEOUT |
30 | | |
31 | 4.57k | #define ANNOUNCE_REQUEST_MIN_SIZE_RECV (ONION_ANNOUNCE_REQUEST_MIN_SIZE + ONION_RETURN_3) |
32 | 2.28k | #define ANNOUNCE_REQUEST_MAX_SIZE_RECV (ONION_ANNOUNCE_REQUEST_MAX_SIZE + ONION_RETURN_3) |
33 | | |
34 | | /* TODO(Jfreegman): DEPRECATE */ |
35 | 33.3k | #define ANNOUNCE_REQUEST_SIZE_RECV (ONION_ANNOUNCE_REQUEST_SIZE + ONION_RETURN_3) |
36 | | |
37 | 10.7k | #define DATA_REQUEST_MIN_SIZE ONION_DATA_REQUEST_MIN_SIZE |
38 | 2.14k | #define DATA_REQUEST_MIN_SIZE_RECV (DATA_REQUEST_MIN_SIZE + ONION_RETURN_3) |
39 | | |
40 | 2.28k | #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 | 1.94k | #define MAX_KEYS_PER_SLOT 4 |
44 | 1.94k | #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 *_Nonnull log; |
59 | | const Mono_Time *_Nonnull mono_time; |
60 | | const Random *_Nonnull rng; |
61 | | const Memory *_Nonnull mem; |
62 | | DHT *_Nonnull dht; |
63 | | Networking_Core *_Nonnull net; |
64 | | Onion_Announce_Entry entries[ONION_ANNOUNCE_MAX_ENTRIES]; |
65 | | uint8_t hmac_key[CRYPTO_HMAC_KEY_SIZE]; |
66 | | |
67 | | Shared_Key_Cache *_Nonnull shared_keys_recv; |
68 | | |
69 | | uint16_t extra_data_max_size; |
70 | | pack_extra_data_cb *_Nullable extra_data_callback; |
71 | | void *_Nullable 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 | 1.83k | { |
77 | 1.83k | onion_a->extra_data_max_size = extra_data_max_size; |
78 | 1.83k | onion_a->extra_data_callback = extra_data_callback; |
79 | 1.83k | onion_a->extra_data_object = extra_data_object; |
80 | 1.83k | } |
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 | 34.1k | { |
111 | 34.1k | if (max_packet_length < ONION_ANNOUNCE_REQUEST_MIN_SIZE) { |
112 | 0 | return -1; |
113 | 0 | } |
114 | | |
115 | 34.1k | uint8_t plain[ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_PUBLIC_KEY_SIZE + |
116 | 34.1k | ONION_ANNOUNCE_SENDBACK_DATA_LENGTH]; |
117 | 34.1k | memcpy(plain, ping_id, ONION_PING_ID_SIZE); |
118 | 34.1k | memcpy(plain + ONION_PING_ID_SIZE, client_id, CRYPTO_PUBLIC_KEY_SIZE); |
119 | 34.1k | memcpy(plain + ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE, data_public_key, CRYPTO_PUBLIC_KEY_SIZE); |
120 | 34.1k | memcpy(plain + ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_PUBLIC_KEY_SIZE, &sendback_data, |
121 | 34.1k | sizeof(sendback_data)); |
122 | | |
123 | 34.1k | packet[0] = NET_PACKET_ANNOUNCE_REQUEST_OLD; |
124 | 34.1k | random_nonce(rng, packet + 1); |
125 | | |
126 | 34.1k | const int len = encrypt_data(mem, dest_client_id, secret_key, packet + 1, plain, sizeof(plain), |
127 | 34.1k | packet + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE); |
128 | | |
129 | 34.1k | if ((uint32_t)len + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE != ONION_ANNOUNCE_REQUEST_MIN_SIZE) { |
130 | 0 | return -1; |
131 | 0 | } |
132 | | |
133 | 34.1k | memcpy(packet + 1 + CRYPTO_NONCE_SIZE, public_key, CRYPTO_PUBLIC_KEY_SIZE); |
134 | | |
135 | 34.1k | return ONION_ANNOUNCE_REQUEST_MIN_SIZE; |
136 | 34.1k | } |
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 | 2.15k | { |
153 | 2.15k | if (DATA_REQUEST_MIN_SIZE + length > max_packet_length) { |
154 | 0 | return -1; |
155 | 0 | } |
156 | | |
157 | 2.15k | if (DATA_REQUEST_MIN_SIZE + length > ONION_MAX_DATA_SIZE) { |
158 | 0 | return -1; |
159 | 0 | } |
160 | | |
161 | 2.15k | packet[0] = NET_PACKET_ONION_DATA_REQUEST; |
162 | 2.15k | memcpy(packet + 1, public_key, CRYPTO_PUBLIC_KEY_SIZE); |
163 | 2.15k | memcpy(packet + 1 + CRYPTO_PUBLIC_KEY_SIZE, nonce, CRYPTO_NONCE_SIZE); |
164 | | |
165 | 2.15k | uint8_t random_public_key[CRYPTO_PUBLIC_KEY_SIZE]; |
166 | 2.15k | uint8_t random_secret_key[CRYPTO_SECRET_KEY_SIZE]; |
167 | 2.15k | crypto_new_keypair(rng, random_public_key, random_secret_key); |
168 | | |
169 | 2.15k | memcpy(packet + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE, random_public_key, CRYPTO_PUBLIC_KEY_SIZE); |
170 | | |
171 | 2.15k | const int len = encrypt_data(mem, encrypt_public_key, random_secret_key, packet + 1 + CRYPTO_PUBLIC_KEY_SIZE, data, length, |
172 | 2.15k | packet + 1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE); |
173 | | |
174 | 2.15k | if (1 + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + len != DATA_REQUEST_MIN_SIZE + |
175 | 2.15k | length) { |
176 | 0 | return -1; |
177 | 0 | } |
178 | | |
179 | 2.15k | return DATA_REQUEST_MIN_SIZE + length; |
180 | 2.15k | } |
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 | 41.3k | { |
274 | 6.44M | for (unsigned int i = 0; i < ONION_ANNOUNCE_MAX_ENTRIES; ++i) { |
275 | 6.42M | if (!mono_time_is_timeout(onion_a->mono_time, onion_a->entries[i].announce_time, ONION_ANNOUNCE_TIMEOUT) |
276 | 6.42M | && pk_equal(onion_a->entries[i].public_key, public_key)) { |
277 | 25.3k | return i; |
278 | 25.3k | } |
279 | 6.42M | } |
280 | | |
281 | 15.9k | return -1; |
282 | 41.3k | } |
283 | | |
284 | | typedef struct Onion_Announce_Entry_Cmp { |
285 | | const Memory *_Nonnull mem; |
286 | | const Mono_Time *_Nonnull mono_time; |
287 | | const uint8_t *_Nonnull 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 | 3.17M | { |
292 | 3.17M | const bool t1 = mono_time_is_timeout(cmp->mono_time, entry1->announce_time, ONION_ANNOUNCE_TIMEOUT); |
293 | 3.17M | const bool t2 = mono_time_is_timeout(cmp->mono_time, entry2->announce_time, ONION_ANNOUNCE_TIMEOUT); |
294 | | |
295 | 3.17M | if (t1 && t2) { |
296 | 3.06M | return 0; |
297 | 3.06M | } |
298 | | |
299 | 109k | if (t1) { |
300 | 157 | return -1; |
301 | 157 | } |
302 | | |
303 | 109k | if (t2) { |
304 | 38.2k | return 1; |
305 | 38.2k | } |
306 | | |
307 | 71.3k | const int closest = id_closest(cmp->comp_public_key, entry1->public_key, entry2->public_key); |
308 | | |
309 | 71.3k | if (closest == 1) { |
310 | 63.6k | return 1; |
311 | 63.6k | } |
312 | | |
313 | 12.5k | if (closest == 2) { |
314 | 12.5k | return -1; |
315 | 12.5k | } |
316 | | |
317 | 18.4E | return 0; |
318 | 7.64k | } |
319 | | |
320 | | static bool onion_announce_entry_less_handler(const void *_Nonnull object, const void *_Nonnull a, const void *_Nonnull b) |
321 | 3.17M | { |
322 | 3.17M | const Onion_Announce_Entry_Cmp *cmp = (const Onion_Announce_Entry_Cmp *)object; |
323 | 3.17M | const Onion_Announce_Entry *entry1 = (const Onion_Announce_Entry *)a; |
324 | 3.17M | const Onion_Announce_Entry *entry2 = (const Onion_Announce_Entry *)b; |
325 | | |
326 | 3.17M | return onion_announce_entry_cmp(cmp, entry1, entry2) < 0; |
327 | 3.17M | } |
328 | | |
329 | | static const void *_Nonnull onion_announce_entry_get_handler(const void *_Nonnull arr, uint32_t index) |
330 | 15.4M | { |
331 | 15.4M | const Onion_Announce_Entry *entries = (const Onion_Announce_Entry *)arr; |
332 | 15.4M | return &entries[index]; |
333 | 15.4M | } |
334 | | |
335 | | static void onion_announce_entry_set_handler(void *_Nonnull arr, uint32_t index, const void *_Nonnull val) |
336 | 12.3M | { |
337 | 12.3M | Onion_Announce_Entry *entries = (Onion_Announce_Entry *)arr; |
338 | 12.3M | const Onion_Announce_Entry *entry = (const Onion_Announce_Entry *)val; |
339 | 12.3M | entries[index] = *entry; |
340 | 12.3M | } |
341 | | |
342 | | static void *_Nonnull onion_announce_entry_subarr_handler(void *_Nonnull arr, uint32_t index, uint32_t size) |
343 | 1.55M | { |
344 | 1.55M | Onion_Announce_Entry *entries = (Onion_Announce_Entry *)arr; |
345 | 1.55M | return &entries[index]; |
346 | 1.55M | } |
347 | | |
348 | | static void *_Nullable onion_announce_entry_alloc_handler(const void *_Nonnull object, uint32_t size) |
349 | 4.84k | { |
350 | 4.84k | const Onion_Announce_Entry_Cmp *cmp = (const Onion_Announce_Entry_Cmp *)object; |
351 | 4.84k | Onion_Announce_Entry *tmp = (Onion_Announce_Entry *)mem_valloc(cmp->mem, size, sizeof(Onion_Announce_Entry)); |
352 | | |
353 | 4.84k | if (tmp == nullptr) { |
354 | 0 | return nullptr; |
355 | 0 | } |
356 | | |
357 | 4.84k | return tmp; |
358 | 4.84k | } |
359 | | |
360 | | static void onion_announce_entry_delete_handler(const void *_Nonnull object, void *_Nonnull arr, uint32_t size) |
361 | 4.84k | { |
362 | 4.84k | const Onion_Announce_Entry_Cmp *cmp = (const Onion_Announce_Entry_Cmp *)object; |
363 | 4.84k | mem_delete(cmp->mem, arr); |
364 | 4.84k | } |
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 | 4.84k | { |
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 | 4.84k | const Onion_Announce_Entry_Cmp cmp = { |
381 | 4.84k | mem, |
382 | 4.84k | mono_time, |
383 | 4.84k | comp_public_key, |
384 | 4.84k | }; |
385 | | |
386 | 4.84k | merge_sort(list, length, &cmp, &onion_announce_entry_cmp_funcs); |
387 | 4.84k | } |
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 | 4.84k | { |
397 | 4.84k | int pos = in_entries(onion_a, public_key); |
398 | | |
399 | 4.84k | if (pos == -1) { |
400 | 607k | for (unsigned i = 0; i < ONION_ANNOUNCE_MAX_ENTRIES; ++i) { |
401 | 603k | if (mono_time_is_timeout(onion_a->mono_time, onion_a->entries[i].announce_time, ONION_ANNOUNCE_TIMEOUT)) { |
402 | 579k | pos = i; |
403 | 579k | } |
404 | 603k | } |
405 | 3.77k | } |
406 | | |
407 | 4.84k | 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 | 4.84k | if (pos == -1) { |
414 | 0 | return -1; |
415 | 0 | } |
416 | | |
417 | 4.84k | memcpy(onion_a->entries[pos].public_key, public_key, CRYPTO_PUBLIC_KEY_SIZE); |
418 | 4.84k | onion_a->entries[pos].ret_ip_port = *ret_ip_port; |
419 | 4.84k | memcpy(onion_a->entries[pos].ret, ret, ONION_RETURN_3); |
420 | 4.84k | memcpy(onion_a->entries[pos].data_public_key, data_public_key, CRYPTO_PUBLIC_KEY_SIZE); |
421 | 4.84k | onion_a->entries[pos].announce_time = mono_time_get(onion_a->mono_time); |
422 | | |
423 | 4.84k | sort_onion_announce_list(onion_a->mem, onion_a->mono_time, |
424 | 4.84k | onion_a->entries, ONION_ANNOUNCE_MAX_ENTRIES, |
425 | 4.84k | dht_get_self_public_key(onion_a->dht)); |
426 | 4.84k | return in_entries(onion_a, public_key); |
427 | 4.84k | } |
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 | 34.3k | { |
432 | 34.3k | if (index < 0) { |
433 | 12.1k | response[0] = 0; |
434 | 12.1k | memcpy(response + 1, ping_id, ONION_PING_ID_SIZE); |
435 | 12.1k | return; |
436 | 12.1k | } |
437 | | |
438 | 22.1k | if (pk_equal(onion_a->entries[index].public_key, packet_public_key)) { |
439 | 13.0k | if (!pk_equal(onion_a->entries[index].data_public_key, data_public_key)) { |
440 | 13 | response[0] = 0; |
441 | 13 | memcpy(response + 1, ping_id, ONION_PING_ID_SIZE); |
442 | 13.0k | } else { |
443 | 13.0k | response[0] = 2; |
444 | 13.0k | memcpy(response + 1, ping_id, ONION_PING_ID_SIZE); |
445 | 13.0k | } |
446 | 13.0k | } else { |
447 | 9.06k | response[0] = 1; |
448 | 9.06k | memcpy(response + 1, onion_a->entries[index].data_public_key, CRYPTO_PUBLIC_KEY_SIZE); |
449 | 9.06k | } |
450 | 22.1k | } |
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 | 34.4k | { |
475 | 34.4k | const uint8_t *packet_public_key = packet + 1 + CRYPTO_NONCE_SIZE; |
476 | 34.4k | const uint8_t *shared_key = shared_key_cache_lookup(onion_a->shared_keys_recv, packet_public_key); |
477 | | |
478 | 34.4k | if (shared_key == nullptr) { |
479 | | /* Error looking up/deriving the shared key */ |
480 | 0 | return 1; |
481 | 0 | } |
482 | | |
483 | 34.4k | uint8_t *plain = (uint8_t *)mem_balloc(onion_a->mem, plain_size); |
484 | | |
485 | 34.4k | if (plain == nullptr) { |
486 | 1 | return 1; |
487 | 1 | } |
488 | | |
489 | 34.4k | const int decrypted_len = decrypt_data_symmetric(onion_a->mem, shared_key, packet + 1, |
490 | 34.4k | packet + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE, plain_size + CRYPTO_MAC_SIZE, plain); |
491 | | |
492 | 34.4k | if ((uint32_t)decrypted_len != plain_size) { |
493 | 127 | mem_delete(onion_a->mem, plain); |
494 | 127 | return 1; |
495 | 127 | } |
496 | | |
497 | 34.3k | const uint16_t ping_id_data_len = CRYPTO_PUBLIC_KEY_SIZE + SIZE_IPPORT; |
498 | 34.3k | uint8_t ping_id_data[CRYPTO_PUBLIC_KEY_SIZE + SIZE_IPPORT]; |
499 | 34.3k | memcpy(ping_id_data, packet_public_key, CRYPTO_PUBLIC_KEY_SIZE); |
500 | 34.3k | const int packed_len = pack_ip_port(onion_a->log, &ping_id_data[CRYPTO_PUBLIC_KEY_SIZE], SIZE_IPPORT, source); |
501 | 34.3k | 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 | 34.3k | assert(packed_len <= SIZE_IPPORT); |
507 | 34.3k | memzero(&ping_id_data[CRYPTO_PUBLIC_KEY_SIZE + packed_len], SIZE_IPPORT - packed_len); |
508 | 34.3k | const uint8_t *data_public_key = plain + ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE; |
509 | | |
510 | 34.3k | int index; |
511 | | |
512 | 34.3k | if (check_timed_auth(onion_a->mono_time, PING_ID_TIMEOUT, onion_a->hmac_key, |
513 | 34.3k | ping_id_data, ping_id_data_len, plain)) { |
514 | 4.84k | index = add_to_entries(onion_a, source, packet_public_key, data_public_key, |
515 | 4.84k | packet + (length - ONION_RETURN_3)); |
516 | 29.4k | } else { |
517 | 29.4k | index = in_entries(onion_a, plain + ONION_PING_ID_SIZE); |
518 | 29.4k | } |
519 | | |
520 | | /* Respond with a announce response packet */ |
521 | 34.3k | Node_format nodes_list[MAX_SENT_NODES]; |
522 | 34.3k | const unsigned int num_nodes = |
523 | 34.3k | get_close_nodes(onion_a->dht, plain + ONION_PING_ID_SIZE, nodes_list, net_family_unspec(), ip_is_lan(&source->ip), false); |
524 | | |
525 | 34.3k | assert(num_nodes <= UINT8_MAX); |
526 | | |
527 | 34.3k | uint8_t nonce[CRYPTO_NONCE_SIZE]; |
528 | 34.3k | random_nonce(onion_a->rng, nonce); |
529 | | |
530 | 34.3k | const uint16_t nodes_offset = 1 + ONION_PING_ID_SIZE + (want_node_count ? 1 : 0); |
531 | 34.3k | const uint16_t response_size = nodes_offset |
532 | 34.3k | + MAX_SENT_NODES * PACKED_NODE_SIZE_IP6 |
533 | 34.3k | + max_extra_size; |
534 | 34.3k | uint8_t *response = (uint8_t *)mem_balloc(onion_a->mem, response_size); |
535 | | |
536 | 34.3k | if (response == nullptr) { |
537 | 1 | mem_delete(onion_a->mem, plain); |
538 | 1 | return 1; |
539 | 1 | } |
540 | | |
541 | 34.3k | uint8_t ping_id[TIMED_AUTH_SIZE]; |
542 | 34.3k | generate_timed_auth(onion_a->mono_time, PING_ID_TIMEOUT, onion_a->hmac_key, |
543 | 34.3k | ping_id_data, ping_id_data_len, ping_id); |
544 | | |
545 | 34.3k | make_announce_payload_helper(onion_a, ping_id, response, index, packet_public_key, data_public_key); |
546 | | |
547 | 34.3k | int nodes_length = 0; |
548 | | |
549 | 34.3k | if (num_nodes != 0) { |
550 | 34.3k | nodes_length = pack_nodes(onion_a->log, &response[nodes_offset], num_nodes * PACKED_NODE_SIZE_IP6, nodes_list, |
551 | 34.3k | (uint16_t)num_nodes); |
552 | | |
553 | 34.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 | 34.3k | } |
560 | | |
561 | 34.3k | uint16_t offset = nodes_offset + nodes_length; |
562 | | |
563 | 34.3k | if (want_node_count) { |
564 | 1.14k | response[1 + ONION_PING_ID_SIZE] = (uint8_t)num_nodes; |
565 | 1.14k | } |
566 | | |
567 | 34.3k | const int extra_size = pack_extra_data_callback == nullptr ? 0 |
568 | 34.3k | : pack_extra_data_callback(onion_a->extra_data_object, |
569 | 1.14k | onion_a->log, onion_a->mem, onion_a->mono_time, num_nodes, |
570 | 1.14k | plain + ONION_MINIMAL_SIZE, length - ANNOUNCE_REQUEST_MIN_SIZE_RECV, |
571 | 1.14k | response, response_size, offset); |
572 | | |
573 | 34.3k | 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 | 34.3k | offset += extra_size; |
580 | | |
581 | | /* Allocate the output buffer to exactly fit the ciphertext we are about |
582 | | * to produce. Using a fixed constant here would be wrong: when the GCA |
583 | | * extra-data callback is active, offset can exceed |
584 | | * ONION_ANNOUNCE_RESPONSE_MAX_SIZE - 33, overflowing any static buffer |
585 | | * sized from that constant. */ |
586 | 34.3k | const uint16_t data_size = (uint16_t)( |
587 | 34.3k | 1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_NONCE_SIZE |
588 | 34.3k | + offset + CRYPTO_MAC_SIZE); |
589 | 34.3k | uint8_t *data = (uint8_t *)mem_balloc(onion_a->mem, data_size); |
590 | | |
591 | 34.3k | if (data == nullptr) { |
592 | 1 | mem_delete(onion_a->mem, response); |
593 | 1 | mem_delete(onion_a->mem, plain); |
594 | 1 | return 1; |
595 | 1 | } |
596 | | |
597 | 34.3k | const int len = encrypt_data_symmetric(onion_a->mem, shared_key, nonce, response, offset, |
598 | 34.3k | data + 1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_NONCE_SIZE); |
599 | | |
600 | 34.3k | if (len != offset + CRYPTO_MAC_SIZE) { |
601 | 1 | LOGGER_ERROR(onion_a->log, "Failed to encrypt announce response"); |
602 | 1 | mem_delete(onion_a->mem, data); |
603 | 1 | mem_delete(onion_a->mem, response); |
604 | 1 | mem_delete(onion_a->mem, plain); |
605 | 1 | return 1; |
606 | 1 | } |
607 | | |
608 | 34.3k | data[0] = response_packet_id; |
609 | 34.3k | memcpy(data + 1, plain + ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE + CRYPTO_PUBLIC_KEY_SIZE, |
610 | 34.3k | ONION_ANNOUNCE_SENDBACK_DATA_LENGTH); |
611 | 34.3k | memcpy(data + 1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH, nonce, CRYPTO_NONCE_SIZE); |
612 | | |
613 | 34.3k | if (send_onion_response(onion_a->log, onion_a->net, source, data, |
614 | 34.3k | 1 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH + CRYPTO_NONCE_SIZE + len, |
615 | 34.3k | packet + (length - ONION_RETURN_3)) == -1) { |
616 | 1 | mem_delete(onion_a->mem, data); |
617 | 1 | mem_delete(onion_a->mem, response); |
618 | 1 | mem_delete(onion_a->mem, plain); |
619 | 1 | return 1; |
620 | 1 | } |
621 | | |
622 | 34.3k | mem_delete(onion_a->mem, data); |
623 | 34.3k | mem_delete(onion_a->mem, response); |
624 | 34.3k | mem_delete(onion_a->mem, plain); |
625 | 34.3k | return 0; |
626 | 34.3k | } |
627 | | |
628 | | static int handle_gca_announce_request(Onion_Announce *_Nonnull onion_a, const IP_Port *_Nonnull source, const uint8_t *_Nonnull packet, uint16_t length) |
629 | 1.14k | { |
630 | 1.14k | if (length > ANNOUNCE_REQUEST_MAX_SIZE_RECV || length <= ANNOUNCE_REQUEST_MIN_SIZE_RECV) { |
631 | 0 | return 1; |
632 | 0 | } |
633 | | |
634 | 1.14k | if (onion_a->extra_data_callback == nullptr) { |
635 | 0 | return 1; |
636 | 0 | } |
637 | | |
638 | 1.14k | return handle_announce_request_common(onion_a, source, packet, length, NET_PACKET_ANNOUNCE_RESPONSE, |
639 | 1.14k | ONION_MINIMAL_SIZE + length - ANNOUNCE_REQUEST_MIN_SIZE_RECV, |
640 | 1.14k | true, onion_a->extra_data_max_size, onion_a->extra_data_callback); |
641 | 1.14k | } |
642 | | |
643 | | static int handle_announce_request(void *_Nonnull object, const IP_Port *_Nonnull source, const uint8_t *_Nonnull packet, uint16_t length, |
644 | | void *_Nullable userdata) |
645 | 1.14k | { |
646 | 1.14k | Onion_Announce *onion_a = (Onion_Announce *)object; |
647 | 1.14k | if (length != ANNOUNCE_REQUEST_MIN_SIZE_RECV) { |
648 | 1.14k | return handle_gca_announce_request(onion_a, source, packet, length); |
649 | 1.14k | } |
650 | | |
651 | 0 | return handle_announce_request_common(onion_a, source, packet, length, NET_PACKET_ANNOUNCE_RESPONSE, |
652 | 0 | ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE * 2 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH, |
653 | 0 | true, 0, nullptr); |
654 | 1.14k | } |
655 | | |
656 | | /* TODO(Jfreegman): DEPRECATE */ |
657 | | static int handle_announce_request_old(void *_Nonnull object, const IP_Port *_Nonnull source, const uint8_t *_Nonnull packet, uint16_t length, |
658 | | void *_Nullable userdata) |
659 | 33.3k | { |
660 | 33.3k | Onion_Announce *onion_a = (Onion_Announce *)object; |
661 | 33.3k | if (length != ANNOUNCE_REQUEST_SIZE_RECV) { |
662 | 0 | return 1; |
663 | 0 | } |
664 | | |
665 | 33.3k | return handle_announce_request_common(onion_a, source, packet, length, NET_PACKET_ANNOUNCE_RESPONSE_OLD, |
666 | 33.3k | ONION_PING_ID_SIZE + CRYPTO_PUBLIC_KEY_SIZE * 2 + ONION_ANNOUNCE_SENDBACK_DATA_LENGTH, |
667 | 33.3k | false, 0, nullptr); |
668 | 33.3k | } |
669 | | |
670 | | static int handle_data_request(void *_Nonnull object, const IP_Port *_Nonnull source, const uint8_t *_Nonnull packet, uint16_t length, void *_Nonnull userdata) |
671 | 2.14k | { |
672 | 2.14k | const Onion_Announce *onion_a = (const Onion_Announce *)object; |
673 | | |
674 | 2.14k | if (length <= DATA_REQUEST_MIN_SIZE_RECV) { |
675 | 0 | return 1; |
676 | 0 | } |
677 | | |
678 | 2.14k | if (length > ONION_MAX_PACKET_SIZE) { |
679 | 0 | return 1; |
680 | 0 | } |
681 | | |
682 | 2.14k | const int index = in_entries(onion_a, packet + 1); |
683 | | |
684 | 2.14k | if (index == -1) { |
685 | 0 | return 1; |
686 | 0 | } |
687 | | |
688 | 2.14k | const uint16_t data_size = length - (CRYPTO_PUBLIC_KEY_SIZE + ONION_RETURN_3); |
689 | 2.14k | VLA(uint8_t, data, data_size); |
690 | 2.14k | data[0] = NET_PACKET_ONION_DATA_RESPONSE; |
691 | 2.14k | memcpy(data + 1, packet + 1 + CRYPTO_PUBLIC_KEY_SIZE, length - (1 + CRYPTO_PUBLIC_KEY_SIZE + ONION_RETURN_3)); |
692 | | |
693 | 2.14k | if (send_onion_response(onion_a->log, onion_a->net, &onion_a->entries[index].ret_ip_port, data, data_size, |
694 | 2.14k | onion_a->entries[index].ret) == -1) { |
695 | 0 | return 1; |
696 | 0 | } |
697 | | |
698 | 2.14k | return 0; |
699 | 2.14k | } |
700 | | |
701 | | Onion_Announce *new_onion_announce(const Logger *log, const Memory *mem, const Random *rng, const Mono_Time *mono_time, DHT *dht, |
702 | | Networking_Core *net) |
703 | 1.95k | { |
704 | 1.95k | if (dht == nullptr) { |
705 | 0 | return nullptr; |
706 | 0 | } |
707 | | |
708 | 1.95k | Onion_Announce *onion_a = (Onion_Announce *)mem_alloc(mem, sizeof(Onion_Announce)); |
709 | | |
710 | 1.95k | if (onion_a == nullptr) { |
711 | 10 | return nullptr; |
712 | 10 | } |
713 | | |
714 | 1.94k | Shared_Key_Cache *const shared_keys_recv = shared_key_cache_new(log, mono_time, mem, dht_get_self_secret_key(dht), KEYS_TIMEOUT, MAX_KEYS_PER_SLOT); |
715 | 1.94k | if (shared_keys_recv == nullptr) { |
716 | 6 | mem_delete(mem, onion_a); |
717 | 6 | return nullptr; |
718 | 6 | } |
719 | 1.93k | onion_a->shared_keys_recv = shared_keys_recv; |
720 | | |
721 | 1.93k | onion_a->log = log; |
722 | 1.93k | onion_a->rng = rng; |
723 | 1.93k | onion_a->mem = mem; |
724 | 1.93k | onion_a->mono_time = mono_time; |
725 | 1.93k | onion_a->dht = dht; |
726 | 1.93k | onion_a->net = net; |
727 | 1.93k | onion_a->extra_data_max_size = 0; |
728 | 1.93k | onion_a->extra_data_callback = nullptr; |
729 | 1.93k | onion_a->extra_data_object = nullptr; |
730 | 1.93k | new_hmac_key(rng, onion_a->hmac_key); |
731 | | |
732 | 1.93k | networking_registerhandler(onion_a->net, NET_PACKET_ANNOUNCE_REQUEST, &handle_announce_request, onion_a); |
733 | 1.93k | networking_registerhandler(onion_a->net, NET_PACKET_ANNOUNCE_REQUEST_OLD, &handle_announce_request_old, onion_a); |
734 | 1.93k | networking_registerhandler(onion_a->net, NET_PACKET_ONION_DATA_REQUEST, &handle_data_request, onion_a); |
735 | | |
736 | 1.93k | return onion_a; |
737 | 1.94k | } |
738 | | |
739 | | void kill_onion_announce(Onion_Announce *onion_a) |
740 | 1.93k | { |
741 | 1.93k | if (onion_a == nullptr) { |
742 | 13 | return; |
743 | 13 | } |
744 | | |
745 | 1.91k | networking_registerhandler(onion_a->net, NET_PACKET_ANNOUNCE_REQUEST, nullptr, nullptr); |
746 | 1.91k | networking_registerhandler(onion_a->net, NET_PACKET_ANNOUNCE_REQUEST_OLD, nullptr, nullptr); |
747 | 1.91k | networking_registerhandler(onion_a->net, NET_PACKET_ONION_DATA_REQUEST, nullptr, nullptr); |
748 | | |
749 | 1.91k | crypto_memzero(onion_a->hmac_key, CRYPTO_HMAC_KEY_SIZE); |
750 | 1.91k | shared_key_cache_free(onion_a->shared_keys_recv); |
751 | | |
752 | 1.91k | mem_delete(onion_a->mem, onion_a); |
753 | 1.91k | } |