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