/src/c-toxcore/toxcore/onion.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 onion part of docs/Prevent_Tracking.txt |
8 | | */ |
9 | | #include "onion.h" |
10 | | |
11 | | #include <assert.h> |
12 | | #include <string.h> |
13 | | |
14 | | #include "DHT.h" |
15 | | #include "attributes.h" |
16 | | #include "ccompat.h" |
17 | | #include "crypto_core.h" |
18 | | #include "logger.h" |
19 | | #include "mem.h" |
20 | | #include "mono_time.h" |
21 | | #include "network.h" |
22 | | #include "shared_key_cache.h" |
23 | | #include "util.h" |
24 | | |
25 | 0 | #define RETURN_1 ONION_RETURN_1 |
26 | 0 | #define RETURN_2 ONION_RETURN_2 |
27 | 0 | #define RETURN_3 ONION_RETURN_3 |
28 | | |
29 | 0 | #define SEND_BASE ONION_SEND_BASE |
30 | 0 | #define SEND_3 ONION_SEND_3 |
31 | 0 | #define SEND_2 ONION_SEND_2 |
32 | 0 | #define SEND_1 ONION_SEND_1 |
33 | | |
34 | 0 | #define KEY_REFRESH_INTERVAL (2 * 60 * 60) |
35 | | |
36 | | // Settings for the shared key cache |
37 | 5.90k | #define MAX_KEYS_PER_SLOT 4 |
38 | 5.90k | #define KEYS_TIMEOUT 600 |
39 | | |
40 | | /** Change symmetric keys every 2 hours to make paths expire eventually. */ |
41 | | static void change_symmetric_key(Onion *_Nonnull onion) |
42 | 0 | { |
43 | 0 | if (mono_time_is_timeout(onion->mono_time, onion->timestamp, KEY_REFRESH_INTERVAL)) { |
44 | 0 | new_symmetric_key(onion->rng, onion->secret_symmetric_key); |
45 | 0 | onion->timestamp = mono_time_get(onion->mono_time); |
46 | 0 | } |
47 | 0 | } |
48 | | |
49 | | /** packing and unpacking functions */ |
50 | | static void ip_pack_to_bytes(uint8_t *_Nonnull data, const IP *_Nonnull source) |
51 | 0 | { |
52 | 0 | data[0] = source->family.value; |
53 | |
|
54 | 0 | if (net_family_is_ipv4(source->family) || net_family_is_tox_tcp_ipv4(source->family)) { |
55 | 0 | memzero(data + 1, SIZE_IP6); |
56 | 0 | memcpy(data + 1, source->ip.v4.uint8, SIZE_IP4); |
57 | 0 | } else { |
58 | 0 | memcpy(data + 1, source->ip.v6.uint8, SIZE_IP6); |
59 | 0 | } |
60 | 0 | } |
61 | | |
62 | | /** return 0 on success, -1 on failure. */ |
63 | | static int ip_unpack_from_bytes(IP *_Nonnull target, const uint8_t *_Nonnull data, unsigned int data_size, bool disable_family_check) |
64 | 0 | { |
65 | 0 | if (data_size < (1 + SIZE_IP6)) { |
66 | 0 | return -1; |
67 | 0 | } |
68 | | |
69 | | // TODO(iphydf): Validate input. |
70 | 0 | target->family.value = data[0]; |
71 | |
|
72 | 0 | if (net_family_is_ipv4(target->family) || net_family_is_tox_tcp_ipv4(target->family)) { |
73 | 0 | memcpy(target->ip.v4.uint8, data + 1, SIZE_IP4); |
74 | 0 | } else { |
75 | 0 | memcpy(target->ip.v6.uint8, data + 1, SIZE_IP6); |
76 | 0 | } |
77 | |
|
78 | 0 | const bool valid = disable_family_check || |
79 | 0 | net_family_is_ipv4(target->family) || |
80 | 0 | net_family_is_ipv6(target->family); |
81 | |
|
82 | 0 | return valid ? 0 : -1; |
83 | 0 | } |
84 | | |
85 | | static void ipport_pack(uint8_t *_Nonnull data, const IP_Port *_Nonnull source) |
86 | 0 | { |
87 | 0 | ip_pack_to_bytes(data, &source->ip); |
88 | 0 | memcpy(data + SIZE_IP, &source->port, SIZE_PORT); |
89 | 0 | } |
90 | | |
91 | | /** return 0 on success, -1 on failure. */ |
92 | | static int ipport_unpack(IP_Port *_Nonnull target, const uint8_t *_Nonnull data, unsigned int data_size, bool disable_family_check) |
93 | 0 | { |
94 | 0 | if (data_size < (SIZE_IP + SIZE_PORT)) { |
95 | 0 | return -1; |
96 | 0 | } |
97 | | |
98 | 0 | if (ip_unpack_from_bytes(&target->ip, data, data_size, disable_family_check) == -1) { |
99 | 0 | return -1; |
100 | 0 | } |
101 | | |
102 | 0 | memcpy(&target->port, data + SIZE_IP, SIZE_PORT); |
103 | 0 | return 0; |
104 | 0 | } |
105 | | |
106 | | /** @brief Create a new onion path. |
107 | | * |
108 | | * Create a new onion path out of nodes (nodes is a list of ONION_PATH_LENGTH nodes) |
109 | | * |
110 | | * new_path must be an empty memory location of at least Onion_Path size. |
111 | | * |
112 | | * return -1 on failure. |
113 | | * return 0 on success. |
114 | | */ |
115 | | int create_onion_path(const Random *rng, const DHT *dht, Onion_Path *new_path, const Node_format *nodes) |
116 | 0 | { |
117 | 0 | if (new_path == nullptr || nodes == nullptr) { |
118 | 0 | return -1; |
119 | 0 | } |
120 | | |
121 | 0 | encrypt_precompute(nodes[0].public_key, dht_get_self_secret_key(dht), new_path->shared_key1); |
122 | 0 | memcpy(new_path->public_key1, dht_get_self_public_key(dht), CRYPTO_PUBLIC_KEY_SIZE); |
123 | |
|
124 | 0 | uint8_t random_public_key[CRYPTO_PUBLIC_KEY_SIZE]; |
125 | 0 | uint8_t random_secret_key[CRYPTO_SECRET_KEY_SIZE]; |
126 | |
|
127 | 0 | crypto_new_keypair(rng, random_public_key, random_secret_key); |
128 | 0 | encrypt_precompute(nodes[1].public_key, random_secret_key, new_path->shared_key2); |
129 | 0 | memcpy(new_path->public_key2, random_public_key, CRYPTO_PUBLIC_KEY_SIZE); |
130 | |
|
131 | 0 | crypto_new_keypair(rng, random_public_key, random_secret_key); |
132 | 0 | encrypt_precompute(nodes[2].public_key, random_secret_key, new_path->shared_key3); |
133 | 0 | memcpy(new_path->public_key3, random_public_key, CRYPTO_PUBLIC_KEY_SIZE); |
134 | |
|
135 | 0 | crypto_memzero(random_secret_key, sizeof(random_secret_key)); |
136 | |
|
137 | 0 | new_path->ip_port1 = nodes[0].ip_port; |
138 | 0 | new_path->ip_port2 = nodes[1].ip_port; |
139 | 0 | new_path->ip_port3 = nodes[2].ip_port; |
140 | |
|
141 | 0 | memcpy(new_path->node_public_key1, nodes[0].public_key, CRYPTO_PUBLIC_KEY_SIZE); |
142 | 0 | memcpy(new_path->node_public_key2, nodes[1].public_key, CRYPTO_PUBLIC_KEY_SIZE); |
143 | 0 | memcpy(new_path->node_public_key3, nodes[2].public_key, CRYPTO_PUBLIC_KEY_SIZE); |
144 | |
|
145 | 0 | return 0; |
146 | 0 | } |
147 | | |
148 | | /** @brief Dump nodes in onion path to nodes of length num_nodes. |
149 | | * |
150 | | * return -1 on failure. |
151 | | * return 0 on success. |
152 | | */ |
153 | | int onion_path_to_nodes(Node_format *nodes, unsigned int num_nodes, const Onion_Path *path) |
154 | 0 | { |
155 | 0 | if (num_nodes < ONION_PATH_LENGTH) { |
156 | 0 | return -1; |
157 | 0 | } |
158 | | |
159 | 0 | nodes[0].ip_port = path->ip_port1; |
160 | 0 | nodes[1].ip_port = path->ip_port2; |
161 | 0 | nodes[2].ip_port = path->ip_port3; |
162 | |
|
163 | 0 | memcpy(nodes[0].public_key, path->node_public_key1, CRYPTO_PUBLIC_KEY_SIZE); |
164 | 0 | memcpy(nodes[1].public_key, path->node_public_key2, CRYPTO_PUBLIC_KEY_SIZE); |
165 | 0 | memcpy(nodes[2].public_key, path->node_public_key3, CRYPTO_PUBLIC_KEY_SIZE); |
166 | 0 | return 0; |
167 | 0 | } |
168 | | |
169 | | /** @brief Create a onion packet. |
170 | | * |
171 | | * Use Onion_Path path to create packet for data of length to dest. |
172 | | * Maximum length of data is ONION_MAX_DATA_SIZE. |
173 | | * packet should be at least ONION_MAX_PACKET_SIZE big. |
174 | | * |
175 | | * return -1 on failure. |
176 | | * return length of created packet on success. |
177 | | */ |
178 | | int create_onion_packet(const Memory *mem, const Random *rng, uint8_t *packet, uint16_t max_packet_length, |
179 | | const Onion_Path *path, const IP_Port *dest, |
180 | | const uint8_t *data, uint16_t length) |
181 | 0 | { |
182 | 0 | if (1 + length + SEND_1 > max_packet_length || length == 0) { |
183 | 0 | return -1; |
184 | 0 | } |
185 | | |
186 | 0 | const uint16_t step1_size = SIZE_IPPORT + length; |
187 | 0 | VLA(uint8_t, step1, step1_size); |
188 | |
|
189 | 0 | ipport_pack(step1, dest); |
190 | 0 | memcpy(step1 + SIZE_IPPORT, data, length); |
191 | |
|
192 | 0 | uint8_t nonce[CRYPTO_NONCE_SIZE]; |
193 | 0 | random_nonce(rng, nonce); |
194 | |
|
195 | 0 | const uint16_t step2_size = SIZE_IPPORT + SEND_BASE + length; |
196 | 0 | VLA(uint8_t, step2, step2_size); |
197 | 0 | ipport_pack(step2, &path->ip_port3); |
198 | 0 | memcpy(step2 + SIZE_IPPORT, path->public_key3, CRYPTO_PUBLIC_KEY_SIZE); |
199 | |
|
200 | 0 | int len = encrypt_data_symmetric(mem, path->shared_key3, nonce, step1, step1_size, |
201 | 0 | step2 + SIZE_IPPORT + CRYPTO_PUBLIC_KEY_SIZE); |
202 | |
|
203 | 0 | if (len != SIZE_IPPORT + length + CRYPTO_MAC_SIZE) { |
204 | 0 | return -1; |
205 | 0 | } |
206 | | |
207 | 0 | const uint16_t step3_size = SIZE_IPPORT + SEND_BASE * 2 + length; |
208 | 0 | VLA(uint8_t, step3, step3_size); |
209 | 0 | ipport_pack(step3, &path->ip_port2); |
210 | 0 | memcpy(step3 + SIZE_IPPORT, path->public_key2, CRYPTO_PUBLIC_KEY_SIZE); |
211 | 0 | len = encrypt_data_symmetric(mem, path->shared_key2, nonce, step2, step2_size, |
212 | 0 | step3 + SIZE_IPPORT + CRYPTO_PUBLIC_KEY_SIZE); |
213 | |
|
214 | 0 | if (len != SIZE_IPPORT + SEND_BASE + length + CRYPTO_MAC_SIZE) { |
215 | 0 | return -1; |
216 | 0 | } |
217 | | |
218 | 0 | packet[0] = NET_PACKET_ONION_SEND_INITIAL; |
219 | 0 | memcpy(packet + 1, nonce, CRYPTO_NONCE_SIZE); |
220 | 0 | memcpy(packet + 1 + CRYPTO_NONCE_SIZE, path->public_key1, CRYPTO_PUBLIC_KEY_SIZE); |
221 | |
|
222 | 0 | len = encrypt_data_symmetric(mem, path->shared_key1, nonce, step3, step3_size, |
223 | 0 | packet + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE); |
224 | |
|
225 | 0 | if (len != SIZE_IPPORT + SEND_BASE * 2 + length + CRYPTO_MAC_SIZE) { |
226 | 0 | return -1; |
227 | 0 | } |
228 | | |
229 | 0 | return 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + len; |
230 | 0 | } |
231 | | |
232 | | /** @brief Create a onion packet to be sent over tcp. |
233 | | * |
234 | | * Use Onion_Path path to create packet for data of length to dest. |
235 | | * Maximum length of data is ONION_MAX_DATA_SIZE. |
236 | | * packet should be at least ONION_MAX_PACKET_SIZE big. |
237 | | * |
238 | | * return -1 on failure. |
239 | | * return length of created packet on success. |
240 | | */ |
241 | | int create_onion_packet_tcp(const Memory *mem, const Random *rng, uint8_t *packet, uint16_t max_packet_length, |
242 | | const Onion_Path *path, const IP_Port *dest, |
243 | | const uint8_t *data, uint16_t length) |
244 | 0 | { |
245 | 0 | if (CRYPTO_NONCE_SIZE + SIZE_IPPORT + SEND_BASE * 2 + length > max_packet_length || length == 0) { |
246 | 0 | return -1; |
247 | 0 | } |
248 | | |
249 | 0 | const uint16_t step1_size = SIZE_IPPORT + length; |
250 | 0 | VLA(uint8_t, step1, step1_size); |
251 | |
|
252 | 0 | ipport_pack(step1, dest); |
253 | 0 | memcpy(step1 + SIZE_IPPORT, data, length); |
254 | |
|
255 | 0 | uint8_t nonce[CRYPTO_NONCE_SIZE]; |
256 | 0 | random_nonce(rng, nonce); |
257 | |
|
258 | 0 | const uint16_t step2_size = SIZE_IPPORT + SEND_BASE + length; |
259 | 0 | VLA(uint8_t, step2, step2_size); |
260 | 0 | ipport_pack(step2, &path->ip_port3); |
261 | 0 | memcpy(step2 + SIZE_IPPORT, path->public_key3, CRYPTO_PUBLIC_KEY_SIZE); |
262 | |
|
263 | 0 | int len = encrypt_data_symmetric(mem, path->shared_key3, nonce, step1, step1_size, |
264 | 0 | step2 + SIZE_IPPORT + CRYPTO_PUBLIC_KEY_SIZE); |
265 | |
|
266 | 0 | if (len != SIZE_IPPORT + length + CRYPTO_MAC_SIZE) { |
267 | 0 | return -1; |
268 | 0 | } |
269 | | |
270 | 0 | ipport_pack(packet + CRYPTO_NONCE_SIZE, &path->ip_port2); |
271 | 0 | memcpy(packet + CRYPTO_NONCE_SIZE + SIZE_IPPORT, path->public_key2, CRYPTO_PUBLIC_KEY_SIZE); |
272 | 0 | len = encrypt_data_symmetric(mem, path->shared_key2, nonce, step2, step2_size, |
273 | 0 | packet + CRYPTO_NONCE_SIZE + SIZE_IPPORT + CRYPTO_PUBLIC_KEY_SIZE); |
274 | |
|
275 | 0 | if (len != SIZE_IPPORT + SEND_BASE + length + CRYPTO_MAC_SIZE) { |
276 | 0 | return -1; |
277 | 0 | } |
278 | | |
279 | 0 | memcpy(packet, nonce, CRYPTO_NONCE_SIZE); |
280 | |
|
281 | 0 | return CRYPTO_NONCE_SIZE + SIZE_IPPORT + CRYPTO_PUBLIC_KEY_SIZE + len; |
282 | 0 | } |
283 | | |
284 | | /** @brief Create and send a onion response sent initially to dest with. |
285 | | * Maximum length of data is ONION_RESPONSE_MAX_DATA_SIZE. |
286 | | * |
287 | | * return -1 on failure. |
288 | | * return 0 on success. |
289 | | */ |
290 | | int send_onion_response(const Logger *log, const Networking_Core *net, |
291 | | const IP_Port *dest, const uint8_t *data, uint16_t length, |
292 | | const uint8_t *ret) |
293 | 0 | { |
294 | 0 | if (length > ONION_RESPONSE_MAX_DATA_SIZE || length == 0) { |
295 | 0 | return -1; |
296 | 0 | } |
297 | | |
298 | 0 | const uint16_t packet_size = 1 + RETURN_3 + length; |
299 | 0 | VLA(uint8_t, packet, packet_size); |
300 | 0 | packet[0] = NET_PACKET_ONION_RECV_3; |
301 | 0 | memcpy(packet + 1, ret, RETURN_3); |
302 | 0 | memcpy(packet + 1 + RETURN_3, data, length); |
303 | |
|
304 | 0 | if ((uint16_t)sendpacket(net, dest, packet, packet_size) != packet_size) { |
305 | 0 | return -1; |
306 | 0 | } |
307 | | |
308 | 0 | Ip_Ntoa ip_str; |
309 | 0 | LOGGER_TRACE(log, "forwarded onion RECV_3 to %s:%d (%02x in %02x, %d bytes)", |
310 | 0 | net_ip_ntoa(&dest->ip, &ip_str), net_ntohs(dest->port), data[0], packet[0], packet_size); |
311 | 0 | return 0; |
312 | 0 | } |
313 | | |
314 | | static int handle_send_initial(void *_Nonnull object, const IP_Port *_Nonnull source, const uint8_t *_Nonnull packet, uint16_t length, void *_Nonnull userdata) |
315 | 0 | { |
316 | 0 | Onion *onion = (Onion *)object; |
317 | |
|
318 | 0 | if (length > ONION_MAX_PACKET_SIZE) { |
319 | 0 | LOGGER_TRACE(onion->log, "invalid initial onion packet length: %u (max: %u)", |
320 | 0 | length, (unsigned int)ONION_MAX_PACKET_SIZE); |
321 | 0 | return 1; |
322 | 0 | } |
323 | | |
324 | 0 | if (length <= 1 + SEND_1) { |
325 | 0 | LOGGER_TRACE(onion->log, "initial onion packet cannot contain SEND_1 packet: %u <= %u", |
326 | 0 | length, (unsigned int)(1 + SEND_1)); |
327 | 0 | return 1; |
328 | 0 | } |
329 | | |
330 | 0 | change_symmetric_key(onion); |
331 | |
|
332 | 0 | const int nonce_start = 1; |
333 | 0 | const int public_key_start = nonce_start + CRYPTO_NONCE_SIZE; |
334 | 0 | const int ciphertext_start = public_key_start + CRYPTO_PUBLIC_KEY_SIZE; |
335 | |
|
336 | 0 | const int ciphertext_length = length - ciphertext_start; |
337 | 0 | const int plaintext_length = ciphertext_length - CRYPTO_MAC_SIZE; |
338 | |
|
339 | 0 | uint8_t plain[ONION_MAX_PACKET_SIZE]; |
340 | 0 | const uint8_t *public_key = &packet[public_key_start]; |
341 | 0 | const uint8_t *shared_key = shared_key_cache_lookup(onion->shared_keys_1, public_key); |
342 | |
|
343 | 0 | if (shared_key == nullptr) { |
344 | | /* Error looking up/deriving the shared key */ |
345 | 0 | LOGGER_TRACE(onion->log, "shared onion key lookup failed for pk %02x%02x...", |
346 | 0 | public_key[0], public_key[1]); |
347 | 0 | return 1; |
348 | 0 | } |
349 | | |
350 | 0 | const int len = decrypt_data_symmetric( |
351 | 0 | onion->mem, shared_key, &packet[nonce_start], &packet[ciphertext_start], ciphertext_length, plain); |
352 | |
|
353 | 0 | if (len != plaintext_length) { |
354 | 0 | LOGGER_TRACE(onion->log, "decrypt failed: %d != %d", len, plaintext_length); |
355 | 0 | return 1; |
356 | 0 | } |
357 | | |
358 | 0 | return onion_send_1(onion, plain, len, source, packet + 1); |
359 | 0 | } |
360 | | |
361 | | int onion_send_1(const Onion *onion, const uint8_t *plain, uint16_t len, const IP_Port *source, const uint8_t *nonce) |
362 | 0 | { |
363 | 0 | const uint16_t max_len = ONION_MAX_PACKET_SIZE + SIZE_IPPORT - (1 + CRYPTO_NONCE_SIZE + ONION_RETURN_1); |
364 | 0 | if (len > max_len) { |
365 | 0 | LOGGER_TRACE(onion->log, "invalid SEND_1 length: %d > %d", len, max_len); |
366 | 0 | return 1; |
367 | 0 | } |
368 | | |
369 | 0 | if (len <= SIZE_IPPORT + SEND_BASE * 2) { |
370 | 0 | return 1; |
371 | 0 | } |
372 | | |
373 | 0 | IP_Port send_to; |
374 | |
|
375 | 0 | if (ipport_unpack(&send_to, plain, len, false) == -1) { |
376 | 0 | return 1; |
377 | 0 | } |
378 | | |
379 | 0 | uint8_t ip_port[SIZE_IPPORT]; |
380 | 0 | ipport_pack(ip_port, source); |
381 | |
|
382 | 0 | uint8_t data[ONION_MAX_PACKET_SIZE] = {0}; |
383 | 0 | data[0] = NET_PACKET_ONION_SEND_1; |
384 | 0 | memcpy(data + 1, nonce, CRYPTO_NONCE_SIZE); |
385 | 0 | memcpy(data + 1 + CRYPTO_NONCE_SIZE, plain + SIZE_IPPORT, len - SIZE_IPPORT); |
386 | 0 | uint16_t data_len = 1 + CRYPTO_NONCE_SIZE + (len - SIZE_IPPORT); |
387 | 0 | uint8_t *ret_part = data + data_len; |
388 | 0 | random_nonce(onion->rng, ret_part); |
389 | 0 | len = encrypt_data_symmetric(onion->mem, onion->secret_symmetric_key, ret_part, ip_port, SIZE_IPPORT, |
390 | 0 | ret_part + CRYPTO_NONCE_SIZE); |
391 | |
|
392 | 0 | if (len != SIZE_IPPORT + CRYPTO_MAC_SIZE) { |
393 | 0 | return 1; |
394 | 0 | } |
395 | | |
396 | 0 | data_len += CRYPTO_NONCE_SIZE + len; |
397 | |
|
398 | 0 | if ((uint32_t)sendpacket(onion->net, &send_to, data, data_len) != data_len) { |
399 | 0 | return 1; |
400 | 0 | } |
401 | | |
402 | 0 | Ip_Ntoa ip_str; |
403 | 0 | LOGGER_TRACE(onion->log, "forwarded onion packet to %s:%d, level 1 (%02x in %02x, %d bytes)", |
404 | 0 | net_ip_ntoa(&send_to.ip, &ip_str), net_ntohs(send_to.port), plain[0], data[0], data_len); |
405 | 0 | return 0; |
406 | 0 | } |
407 | | |
408 | | static int handle_send_1(void *_Nonnull object, const IP_Port *_Nonnull source, const uint8_t *_Nonnull packet, uint16_t length, void *_Nonnull userdata) |
409 | 0 | { |
410 | 0 | Onion *onion = (Onion *)object; |
411 | |
|
412 | 0 | if (length > ONION_MAX_PACKET_SIZE) { |
413 | 0 | return 1; |
414 | 0 | } |
415 | | |
416 | 0 | if (length <= 1 + SEND_2) { |
417 | 0 | return 1; |
418 | 0 | } |
419 | | |
420 | 0 | change_symmetric_key(onion); |
421 | |
|
422 | 0 | uint8_t plain[ONION_MAX_PACKET_SIZE]; |
423 | 0 | const uint8_t *public_key = packet + 1 + CRYPTO_NONCE_SIZE; |
424 | 0 | const uint8_t *shared_key = shared_key_cache_lookup(onion->shared_keys_2, public_key); |
425 | |
|
426 | 0 | if (shared_key == nullptr) { |
427 | | /* Error looking up/deriving the shared key */ |
428 | 0 | return 1; |
429 | 0 | } |
430 | | |
431 | 0 | int len = decrypt_data_symmetric(onion->mem, shared_key, packet + 1, packet + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE, |
432 | 0 | length - (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + RETURN_1), plain); |
433 | |
|
434 | 0 | if (len != length - (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + RETURN_1 + CRYPTO_MAC_SIZE)) { |
435 | 0 | return 1; |
436 | 0 | } |
437 | | |
438 | 0 | IP_Port send_to; |
439 | |
|
440 | 0 | if (ipport_unpack(&send_to, plain, len, false) == -1) { |
441 | 0 | return 1; |
442 | 0 | } |
443 | | |
444 | 0 | uint8_t data[ONION_MAX_PACKET_SIZE] = {0}; |
445 | 0 | data[0] = NET_PACKET_ONION_SEND_2; |
446 | 0 | memcpy(data + 1, packet + 1, CRYPTO_NONCE_SIZE); |
447 | 0 | memcpy(data + 1 + CRYPTO_NONCE_SIZE, plain + SIZE_IPPORT, len - SIZE_IPPORT); |
448 | 0 | uint16_t data_len = 1 + CRYPTO_NONCE_SIZE + (len - SIZE_IPPORT); |
449 | 0 | uint8_t *ret_part = data + data_len; |
450 | 0 | random_nonce(onion->rng, ret_part); |
451 | 0 | uint8_t ret_data[RETURN_1 + SIZE_IPPORT]; |
452 | 0 | ipport_pack(ret_data, source); |
453 | 0 | memcpy(ret_data + SIZE_IPPORT, packet + (length - RETURN_1), RETURN_1); |
454 | 0 | len = encrypt_data_symmetric(onion->mem, onion->secret_symmetric_key, ret_part, ret_data, sizeof(ret_data), |
455 | 0 | ret_part + CRYPTO_NONCE_SIZE); |
456 | |
|
457 | 0 | if (len != RETURN_2 - CRYPTO_NONCE_SIZE) { |
458 | 0 | return 1; |
459 | 0 | } |
460 | | |
461 | 0 | data_len += CRYPTO_NONCE_SIZE + len; |
462 | |
|
463 | 0 | if ((uint32_t)sendpacket(onion->net, &send_to, data, data_len) != data_len) { |
464 | 0 | return 1; |
465 | 0 | } |
466 | | |
467 | 0 | Ip_Ntoa ip_str; |
468 | 0 | LOGGER_TRACE(onion->log, "forwarded onion packet to %s:%d, level 2 (%02x in %02x, %d bytes)", |
469 | 0 | net_ip_ntoa(&send_to.ip, &ip_str), net_ntohs(send_to.port), packet[0], data[0], data_len); |
470 | 0 | return 0; |
471 | 0 | } |
472 | | |
473 | | static int handle_send_2(void *_Nonnull object, const IP_Port *_Nonnull source, const uint8_t *_Nonnull packet, uint16_t length, void *_Nonnull userdata) |
474 | 0 | { |
475 | 0 | Onion *onion = (Onion *)object; |
476 | |
|
477 | 0 | if (length > ONION_MAX_PACKET_SIZE) { |
478 | 0 | return 1; |
479 | 0 | } |
480 | | |
481 | 0 | if (length <= 1 + SEND_3) { |
482 | 0 | return 1; |
483 | 0 | } |
484 | | |
485 | 0 | change_symmetric_key(onion); |
486 | |
|
487 | 0 | uint8_t plain[ONION_MAX_PACKET_SIZE]; |
488 | 0 | const uint8_t *public_key = packet + 1 + CRYPTO_NONCE_SIZE; |
489 | 0 | const uint8_t *shared_key = shared_key_cache_lookup(onion->shared_keys_3, public_key); |
490 | |
|
491 | 0 | if (shared_key == nullptr) { |
492 | | /* Error looking up/deriving the shared key */ |
493 | 0 | return 1; |
494 | 0 | } |
495 | | |
496 | 0 | int len = decrypt_data_symmetric(onion->mem, shared_key, packet + 1, packet + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE, |
497 | 0 | length - (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + RETURN_2), plain); |
498 | |
|
499 | 0 | if (len != length - (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + RETURN_2 + CRYPTO_MAC_SIZE)) { |
500 | 0 | return 1; |
501 | 0 | } |
502 | | |
503 | 0 | assert(len > SIZE_IPPORT); |
504 | | |
505 | 0 | const uint8_t packet_id = plain[SIZE_IPPORT]; |
506 | |
|
507 | 0 | if (packet_id != NET_PACKET_ANNOUNCE_REQUEST && packet_id != NET_PACKET_ANNOUNCE_REQUEST_OLD && |
508 | 0 | packet_id != NET_PACKET_ONION_DATA_REQUEST) { |
509 | 0 | return 1; |
510 | 0 | } |
511 | | |
512 | 0 | IP_Port send_to; |
513 | |
|
514 | 0 | if (ipport_unpack(&send_to, plain, len, false) == -1) { |
515 | 0 | return 1; |
516 | 0 | } |
517 | | |
518 | 0 | uint8_t data[ONION_MAX_PACKET_SIZE] = {0}; |
519 | 0 | memcpy(data, plain + SIZE_IPPORT, len - SIZE_IPPORT); |
520 | 0 | uint16_t data_len = len - SIZE_IPPORT; |
521 | 0 | uint8_t *ret_part = data + (len - SIZE_IPPORT); |
522 | 0 | random_nonce(onion->rng, ret_part); |
523 | 0 | uint8_t ret_data[RETURN_2 + SIZE_IPPORT]; |
524 | 0 | ipport_pack(ret_data, source); |
525 | 0 | memcpy(ret_data + SIZE_IPPORT, packet + (length - RETURN_2), RETURN_2); |
526 | 0 | len = encrypt_data_symmetric(onion->mem, onion->secret_symmetric_key, ret_part, ret_data, sizeof(ret_data), |
527 | 0 | ret_part + CRYPTO_NONCE_SIZE); |
528 | |
|
529 | 0 | if (len != RETURN_3 - CRYPTO_NONCE_SIZE) { |
530 | 0 | return 1; |
531 | 0 | } |
532 | | |
533 | 0 | data_len += RETURN_3; |
534 | |
|
535 | 0 | if ((uint32_t)sendpacket(onion->net, &send_to, data, data_len) != data_len) { |
536 | 0 | return 1; |
537 | 0 | } |
538 | | |
539 | 0 | Ip_Ntoa ip_str; |
540 | 0 | LOGGER_TRACE(onion->log, "forwarded onion packet to %s:%d, level 3 (%02x in %02x, %d bytes)", |
541 | 0 | net_ip_ntoa(&send_to.ip, &ip_str), net_ntohs(send_to.port), packet[0], data[0], data_len); |
542 | 0 | return 0; |
543 | 0 | } |
544 | | |
545 | | static int handle_recv_3(void *_Nonnull object, const IP_Port *_Nonnull source, const uint8_t *_Nonnull packet, uint16_t length, void *_Nonnull userdata) |
546 | 0 | { |
547 | 0 | Onion *onion = (Onion *)object; |
548 | |
|
549 | 0 | if (length > ONION_MAX_PACKET_SIZE) { |
550 | 0 | return 1; |
551 | 0 | } |
552 | | |
553 | 0 | if (length <= 1 + RETURN_3) { |
554 | 0 | return 1; |
555 | 0 | } |
556 | | |
557 | 0 | const uint8_t packet_id = packet[1 + RETURN_3]; |
558 | |
|
559 | 0 | if (packet_id != NET_PACKET_ANNOUNCE_RESPONSE && packet_id != NET_PACKET_ANNOUNCE_RESPONSE_OLD && |
560 | 0 | packet_id != NET_PACKET_ONION_DATA_RESPONSE) { |
561 | 0 | return 1; |
562 | 0 | } |
563 | | |
564 | 0 | change_symmetric_key(onion); |
565 | |
|
566 | 0 | uint8_t plain[SIZE_IPPORT + RETURN_2]; |
567 | 0 | const int len = decrypt_data_symmetric(onion->mem, onion->secret_symmetric_key, packet + 1, packet + 1 + CRYPTO_NONCE_SIZE, |
568 | 0 | SIZE_IPPORT + RETURN_2 + CRYPTO_MAC_SIZE, plain); |
569 | |
|
570 | 0 | if ((uint32_t)len != sizeof(plain)) { |
571 | 0 | return 1; |
572 | 0 | } |
573 | | |
574 | 0 | IP_Port send_to; |
575 | |
|
576 | 0 | if (ipport_unpack(&send_to, plain, len, false) == -1) { |
577 | 0 | LOGGER_DEBUG(onion->log, "failed to unpack IP/Port"); |
578 | 0 | return 1; |
579 | 0 | } |
580 | | |
581 | 0 | uint8_t data[ONION_MAX_PACKET_SIZE] = {0}; |
582 | 0 | data[0] = NET_PACKET_ONION_RECV_2; |
583 | 0 | memcpy(data + 1, plain + SIZE_IPPORT, RETURN_2); |
584 | 0 | memcpy(data + 1 + RETURN_2, packet + 1 + RETURN_3, length - (1 + RETURN_3)); |
585 | 0 | const uint16_t data_len = 1 + RETURN_2 + (length - (1 + RETURN_3)); |
586 | |
|
587 | 0 | if ((uint32_t)sendpacket(onion->net, &send_to, data, data_len) != data_len) { |
588 | 0 | return 1; |
589 | 0 | } |
590 | | |
591 | 0 | Ip_Ntoa ip_str; |
592 | 0 | LOGGER_TRACE(onion->log, "forwarded onion RECV_2 to %s:%d (%02x in %02x, %d bytes)", |
593 | 0 | net_ip_ntoa(&send_to.ip, &ip_str), net_ntohs(send_to.port), packet[0], data[0], data_len); |
594 | 0 | return 0; |
595 | 0 | } |
596 | | |
597 | | static int handle_recv_2(void *_Nonnull object, const IP_Port *_Nonnull source, const uint8_t *_Nonnull packet, uint16_t length, void *_Nonnull userdata) |
598 | 0 | { |
599 | 0 | Onion *onion = (Onion *)object; |
600 | |
|
601 | 0 | if (length > ONION_MAX_PACKET_SIZE) { |
602 | 0 | return 1; |
603 | 0 | } |
604 | | |
605 | 0 | if (length <= 1 + RETURN_2) { |
606 | 0 | return 1; |
607 | 0 | } |
608 | | |
609 | 0 | const uint8_t packet_id = packet[1 + RETURN_2]; |
610 | |
|
611 | 0 | if (packet_id != NET_PACKET_ANNOUNCE_RESPONSE && packet_id != NET_PACKET_ANNOUNCE_RESPONSE_OLD && |
612 | 0 | packet_id != NET_PACKET_ONION_DATA_RESPONSE) { |
613 | 0 | return 1; |
614 | 0 | } |
615 | | |
616 | 0 | change_symmetric_key(onion); |
617 | |
|
618 | 0 | uint8_t plain[SIZE_IPPORT + RETURN_1]; |
619 | 0 | const int len = decrypt_data_symmetric(onion->mem, onion->secret_symmetric_key, packet + 1, packet + 1 + CRYPTO_NONCE_SIZE, |
620 | 0 | SIZE_IPPORT + RETURN_1 + CRYPTO_MAC_SIZE, plain); |
621 | |
|
622 | 0 | if ((uint32_t)len != sizeof(plain)) { |
623 | 0 | return 1; |
624 | 0 | } |
625 | | |
626 | 0 | IP_Port send_to; |
627 | |
|
628 | 0 | if (ipport_unpack(&send_to, plain, len, false) == -1) { |
629 | 0 | return 1; |
630 | 0 | } |
631 | | |
632 | 0 | uint8_t data[ONION_MAX_PACKET_SIZE] = {0}; |
633 | 0 | data[0] = NET_PACKET_ONION_RECV_1; |
634 | 0 | memcpy(data + 1, plain + SIZE_IPPORT, RETURN_1); |
635 | 0 | memcpy(data + 1 + RETURN_1, packet + 1 + RETURN_2, length - (1 + RETURN_2)); |
636 | 0 | const uint16_t data_len = 1 + RETURN_1 + (length - (1 + RETURN_2)); |
637 | |
|
638 | 0 | if ((uint32_t)sendpacket(onion->net, &send_to, data, data_len) != data_len) { |
639 | 0 | return 1; |
640 | 0 | } |
641 | | |
642 | 0 | Ip_Ntoa ip_str; |
643 | 0 | LOGGER_TRACE(onion->log, "forwarded onion RECV_1 to %s:%d (%02x in %02x, %d bytes)", |
644 | 0 | net_ip_ntoa(&send_to.ip, &ip_str), net_ntohs(send_to.port), packet[0], data[0], data_len); |
645 | 0 | return 0; |
646 | 0 | } |
647 | | |
648 | | static int handle_recv_1(void *_Nonnull object, const IP_Port *_Nonnull source, const uint8_t *_Nonnull packet, uint16_t length, void *_Nonnull userdata) |
649 | 0 | { |
650 | 0 | Onion *onion = (Onion *)object; |
651 | |
|
652 | 0 | if (length > ONION_MAX_PACKET_SIZE) { |
653 | 0 | return 1; |
654 | 0 | } |
655 | | |
656 | 0 | if (length <= 1 + RETURN_1) { |
657 | 0 | return 1; |
658 | 0 | } |
659 | | |
660 | 0 | const uint8_t packet_id = packet[1 + RETURN_1]; |
661 | |
|
662 | 0 | if (packet_id != NET_PACKET_ANNOUNCE_RESPONSE && packet_id != NET_PACKET_ANNOUNCE_RESPONSE_OLD && |
663 | 0 | packet_id != NET_PACKET_ONION_DATA_RESPONSE) { |
664 | 0 | return 1; |
665 | 0 | } |
666 | | |
667 | 0 | change_symmetric_key(onion); |
668 | |
|
669 | 0 | uint8_t plain[SIZE_IPPORT]; |
670 | 0 | const int len = decrypt_data_symmetric(onion->mem, onion->secret_symmetric_key, packet + 1, packet + 1 + CRYPTO_NONCE_SIZE, |
671 | 0 | SIZE_IPPORT + CRYPTO_MAC_SIZE, plain); |
672 | |
|
673 | 0 | if ((uint32_t)len != SIZE_IPPORT) { |
674 | 0 | return 1; |
675 | 0 | } |
676 | | |
677 | 0 | IP_Port send_to; |
678 | |
|
679 | 0 | if (ipport_unpack(&send_to, plain, len, true) == -1) { |
680 | 0 | LOGGER_DEBUG(onion->log, "failed to unpack IP/Port"); |
681 | 0 | return 1; |
682 | 0 | } |
683 | | |
684 | 0 | const uint16_t data_len = length - (1 + RETURN_1); |
685 | |
|
686 | 0 | if (onion->recv_1_function != nullptr && |
687 | 0 | !net_family_is_ipv4(send_to.ip.family) && |
688 | 0 | !net_family_is_ipv6(send_to.ip.family)) { |
689 | 0 | return onion->recv_1_function(onion->callback_object, &send_to, packet + (1 + RETURN_1), data_len); |
690 | 0 | } |
691 | | |
692 | 0 | if ((uint32_t)sendpacket(onion->net, &send_to, packet + (1 + RETURN_1), data_len) != data_len) { |
693 | 0 | return 1; |
694 | 0 | } |
695 | | |
696 | 0 | return 0; |
697 | 0 | } |
698 | | |
699 | | void set_callback_handle_recv_1(Onion *onion, onion_recv_1_cb *function, void *object) |
700 | 0 | { |
701 | 0 | onion->recv_1_function = function; |
702 | 0 | onion->callback_object = object; |
703 | 0 | } |
704 | | |
705 | | Onion *new_onion(const Logger *log, const Memory *mem, const Mono_Time *mono_time, const Random *rng, DHT *dht) |
706 | 1.96k | { |
707 | 1.96k | if (dht == nullptr) { |
708 | 0 | return nullptr; |
709 | 0 | } |
710 | | |
711 | 1.96k | Onion *onion = (Onion *)mem_alloc(mem, sizeof(Onion)); |
712 | | |
713 | 1.96k | if (onion == nullptr) { |
714 | 0 | return nullptr; |
715 | 0 | } |
716 | | |
717 | 1.96k | onion->log = log; |
718 | 1.96k | onion->dht = dht; |
719 | 1.96k | onion->net = dht_get_net(dht); |
720 | 1.96k | onion->mono_time = mono_time; |
721 | 1.96k | onion->rng = rng; |
722 | 1.96k | onion->mem = mem; |
723 | 1.96k | new_symmetric_key(rng, onion->secret_symmetric_key); |
724 | 1.96k | onion->timestamp = mono_time_get(onion->mono_time); |
725 | | |
726 | 1.96k | const uint8_t *secret_key = dht_get_self_secret_key(dht); |
727 | 1.96k | onion->shared_keys_1 = shared_key_cache_new(log, mono_time, mem, secret_key, KEYS_TIMEOUT, MAX_KEYS_PER_SLOT); |
728 | 1.96k | onion->shared_keys_2 = shared_key_cache_new(log, mono_time, mem, secret_key, KEYS_TIMEOUT, MAX_KEYS_PER_SLOT); |
729 | 1.96k | onion->shared_keys_3 = shared_key_cache_new(log, mono_time, mem, secret_key, KEYS_TIMEOUT, MAX_KEYS_PER_SLOT); |
730 | | |
731 | 1.96k | if (onion->shared_keys_1 == nullptr || |
732 | 1.96k | onion->shared_keys_2 == nullptr || |
733 | 1.96k | onion->shared_keys_3 == nullptr) { |
734 | | // cppcheck-suppress mismatchAllocDealloc |
735 | 0 | kill_onion(onion); |
736 | 0 | return nullptr; |
737 | 0 | } |
738 | | |
739 | 1.96k | networking_registerhandler(onion->net, NET_PACKET_ONION_SEND_INITIAL, &handle_send_initial, onion); |
740 | 1.96k | networking_registerhandler(onion->net, NET_PACKET_ONION_SEND_1, &handle_send_1, onion); |
741 | 1.96k | networking_registerhandler(onion->net, NET_PACKET_ONION_SEND_2, &handle_send_2, onion); |
742 | | |
743 | 1.96k | networking_registerhandler(onion->net, NET_PACKET_ONION_RECV_3, &handle_recv_3, onion); |
744 | 1.96k | networking_registerhandler(onion->net, NET_PACKET_ONION_RECV_2, &handle_recv_2, onion); |
745 | 1.96k | networking_registerhandler(onion->net, NET_PACKET_ONION_RECV_1, &handle_recv_1, onion); |
746 | | |
747 | 1.96k | return onion; |
748 | 1.96k | } |
749 | | |
750 | | void kill_onion(Onion *onion) |
751 | 1.96k | { |
752 | 1.96k | if (onion == nullptr) { |
753 | 0 | return; |
754 | 0 | } |
755 | | |
756 | 1.96k | networking_registerhandler(onion->net, NET_PACKET_ONION_SEND_INITIAL, nullptr, nullptr); |
757 | 1.96k | networking_registerhandler(onion->net, NET_PACKET_ONION_SEND_1, nullptr, nullptr); |
758 | 1.96k | networking_registerhandler(onion->net, NET_PACKET_ONION_SEND_2, nullptr, nullptr); |
759 | | |
760 | 1.96k | networking_registerhandler(onion->net, NET_PACKET_ONION_RECV_3, nullptr, nullptr); |
761 | 1.96k | networking_registerhandler(onion->net, NET_PACKET_ONION_RECV_2, nullptr, nullptr); |
762 | 1.96k | networking_registerhandler(onion->net, NET_PACKET_ONION_RECV_1, nullptr, nullptr); |
763 | | |
764 | 1.96k | crypto_memzero(onion->secret_symmetric_key, sizeof(onion->secret_symmetric_key)); |
765 | | |
766 | 1.96k | shared_key_cache_free(onion->shared_keys_1); |
767 | 1.96k | shared_key_cache_free(onion->shared_keys_2); |
768 | 1.96k | shared_key_cache_free(onion->shared_keys_3); |
769 | | |
770 | 1.96k | mem_delete(onion->mem, onion); |
771 | 1.96k | } |