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 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 | 437k | #define RETURN_1 ONION_RETURN_1 |
26 | 510k | #define RETURN_2 ONION_RETURN_2 |
27 | 364k | #define RETURN_3 ONION_RETURN_3 |
28 | | |
29 | 180k | #define SEND_BASE ONION_SEND_BASE |
30 | 36.7k | #define SEND_3 ONION_SEND_3 |
31 | 36.8k | #define SEND_2 ONION_SEND_2 |
32 | 61.5k | #define SEND_1 ONION_SEND_1 |
33 | | |
34 | 213k | #define KEY_REFRESH_INTERVAL (2 * 60 * 60) |
35 | | |
36 | | // Settings for the shared key cache |
37 | 5.82k | #define MAX_KEYS_PER_SLOT 4 |
38 | 5.82k | #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 | 213k | { |
43 | 213k | 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 | 213k | } |
48 | | |
49 | | /** packing and unpacking functions */ |
50 | | static void ip_pack_to_bytes(uint8_t *_Nonnull data, const IP *_Nonnull source) |
51 | 222k | { |
52 | 222k | data[0] = source->family.value; |
53 | | |
54 | 222k | if (net_family_is_ipv4(source->family) || net_family_is_tox_tcp_ipv4(source->family)) { |
55 | 216k | memzero(data + 1, SIZE_IP6); |
56 | 216k | memcpy(data + 1, source->ip.v4.uint8, SIZE_IP4); |
57 | 216k | } else { |
58 | 6.55k | memcpy(data + 1, source->ip.v6.uint8, SIZE_IP6); |
59 | 6.55k | } |
60 | 222k | } |
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 | 219k | { |
65 | 219k | if (data_size < (1 + SIZE_IP6)) { |
66 | 0 | return -1; |
67 | 0 | } |
68 | | |
69 | | // TODO(iphydf): Validate input. |
70 | 219k | target->family.value = data[0]; |
71 | | |
72 | 219k | if (net_family_is_ipv4(target->family) || net_family_is_tox_tcp_ipv4(target->family)) { |
73 | 212k | memcpy(target->ip.v4.uint8, data + 1, SIZE_IP4); |
74 | 212k | } else { |
75 | 6.54k | memcpy(target->ip.v6.uint8, data + 1, SIZE_IP6); |
76 | 6.54k | } |
77 | | |
78 | 219k | const bool valid = disable_family_check || |
79 | 219k | net_family_is_ipv4(target->family) || |
80 | 219k | net_family_is_ipv6(target->family); |
81 | | |
82 | 219k | return valid ? 0 : -1; |
83 | 219k | } |
84 | | |
85 | | static void ipport_pack(uint8_t *_Nonnull data, const IP_Port *_Nonnull source) |
86 | 222k | { |
87 | 222k | ip_pack_to_bytes(data, &source->ip); |
88 | 222k | memcpy(data + SIZE_IP, &source->port, SIZE_PORT); |
89 | 222k | } |
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 | 219k | { |
94 | 219k | if (data_size < (SIZE_IP + SIZE_PORT)) { |
95 | 0 | return -1; |
96 | 0 | } |
97 | | |
98 | 219k | if (ip_unpack_from_bytes(&target->ip, data, data_size, disable_family_check) == -1) { |
99 | 0 | return -1; |
100 | 0 | } |
101 | | |
102 | 219k | memcpy(&target->port, data + SIZE_IP, SIZE_PORT); |
103 | 219k | return 0; |
104 | 219k | } |
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 | 3.73k | { |
117 | 3.73k | if (new_path == nullptr || nodes == nullptr) { |
118 | 0 | return -1; |
119 | 0 | } |
120 | | |
121 | 3.73k | encrypt_precompute(nodes[0].public_key, dht_get_self_secret_key(dht), new_path->shared_key1); |
122 | 3.73k | memcpy(new_path->public_key1, dht_get_self_public_key(dht), CRYPTO_PUBLIC_KEY_SIZE); |
123 | | |
124 | 3.73k | uint8_t random_public_key[CRYPTO_PUBLIC_KEY_SIZE]; |
125 | 3.73k | uint8_t random_secret_key[CRYPTO_SECRET_KEY_SIZE]; |
126 | | |
127 | 3.73k | crypto_new_keypair(rng, random_public_key, random_secret_key); |
128 | 3.73k | encrypt_precompute(nodes[1].public_key, random_secret_key, new_path->shared_key2); |
129 | 3.73k | memcpy(new_path->public_key2, random_public_key, CRYPTO_PUBLIC_KEY_SIZE); |
130 | | |
131 | 3.73k | crypto_new_keypair(rng, random_public_key, random_secret_key); |
132 | 3.73k | encrypt_precompute(nodes[2].public_key, random_secret_key, new_path->shared_key3); |
133 | 3.73k | memcpy(new_path->public_key3, random_public_key, CRYPTO_PUBLIC_KEY_SIZE); |
134 | | |
135 | 3.73k | crypto_memzero(random_secret_key, sizeof(random_secret_key)); |
136 | | |
137 | 3.73k | new_path->ip_port1 = nodes[0].ip_port; |
138 | 3.73k | new_path->ip_port2 = nodes[1].ip_port; |
139 | 3.73k | new_path->ip_port3 = nodes[2].ip_port; |
140 | | |
141 | 3.73k | memcpy(new_path->node_public_key1, nodes[0].public_key, CRYPTO_PUBLIC_KEY_SIZE); |
142 | 3.73k | memcpy(new_path->node_public_key2, nodes[1].public_key, CRYPTO_PUBLIC_KEY_SIZE); |
143 | 3.73k | memcpy(new_path->node_public_key3, nodes[2].public_key, CRYPTO_PUBLIC_KEY_SIZE); |
144 | | |
145 | 3.73k | return 0; |
146 | 3.73k | } |
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 | 33.0k | { |
155 | 33.0k | if (num_nodes < ONION_PATH_LENGTH) { |
156 | 0 | return -1; |
157 | 0 | } |
158 | | |
159 | 33.0k | nodes[0].ip_port = path->ip_port1; |
160 | 33.0k | nodes[1].ip_port = path->ip_port2; |
161 | 33.0k | nodes[2].ip_port = path->ip_port3; |
162 | | |
163 | 33.0k | memcpy(nodes[0].public_key, path->node_public_key1, CRYPTO_PUBLIC_KEY_SIZE); |
164 | 33.0k | memcpy(nodes[1].public_key, path->node_public_key2, CRYPTO_PUBLIC_KEY_SIZE); |
165 | 33.0k | memcpy(nodes[2].public_key, path->node_public_key3, CRYPTO_PUBLIC_KEY_SIZE); |
166 | 33.0k | return 0; |
167 | 33.0k | } |
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 | 30.8k | { |
182 | 30.8k | if (1 + length + SEND_1 > max_packet_length || length == 0) { |
183 | 0 | return -1; |
184 | 0 | } |
185 | | |
186 | 30.8k | const uint16_t step1_size = SIZE_IPPORT + length; |
187 | 30.8k | VLA(uint8_t, step1, step1_size); |
188 | | |
189 | 30.8k | ipport_pack(step1, dest); |
190 | 30.8k | memcpy(step1 + SIZE_IPPORT, data, length); |
191 | | |
192 | 30.8k | uint8_t nonce[CRYPTO_NONCE_SIZE]; |
193 | 30.8k | random_nonce(rng, nonce); |
194 | | |
195 | 30.8k | const uint16_t step2_size = SIZE_IPPORT + SEND_BASE + length; |
196 | 30.8k | VLA(uint8_t, step2, step2_size); |
197 | 30.8k | ipport_pack(step2, &path->ip_port3); |
198 | 30.8k | memcpy(step2 + SIZE_IPPORT, path->public_key3, CRYPTO_PUBLIC_KEY_SIZE); |
199 | | |
200 | 30.8k | int len = encrypt_data_symmetric(mem, path->shared_key3, nonce, step1, step1_size, |
201 | 30.8k | step2 + SIZE_IPPORT + CRYPTO_PUBLIC_KEY_SIZE); |
202 | | |
203 | 30.8k | if (len != SIZE_IPPORT + length + CRYPTO_MAC_SIZE) { |
204 | 0 | return -1; |
205 | 0 | } |
206 | | |
207 | 30.8k | const uint16_t step3_size = SIZE_IPPORT + SEND_BASE * 2 + length; |
208 | 30.8k | VLA(uint8_t, step3, step3_size); |
209 | 30.8k | ipport_pack(step3, &path->ip_port2); |
210 | 30.8k | memcpy(step3 + SIZE_IPPORT, path->public_key2, CRYPTO_PUBLIC_KEY_SIZE); |
211 | 30.8k | len = encrypt_data_symmetric(mem, path->shared_key2, nonce, step2, step2_size, |
212 | 30.8k | step3 + SIZE_IPPORT + CRYPTO_PUBLIC_KEY_SIZE); |
213 | | |
214 | 30.8k | if (len != SIZE_IPPORT + SEND_BASE + length + CRYPTO_MAC_SIZE) { |
215 | 0 | return -1; |
216 | 0 | } |
217 | | |
218 | 30.8k | packet[0] = NET_PACKET_ONION_SEND_INITIAL; |
219 | 30.8k | memcpy(packet + 1, nonce, CRYPTO_NONCE_SIZE); |
220 | 30.8k | memcpy(packet + 1 + CRYPTO_NONCE_SIZE, path->public_key1, CRYPTO_PUBLIC_KEY_SIZE); |
221 | | |
222 | 30.8k | len = encrypt_data_symmetric(mem, path->shared_key1, nonce, step3, step3_size, |
223 | 30.8k | packet + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE); |
224 | | |
225 | 30.8k | if (len != SIZE_IPPORT + SEND_BASE * 2 + length + CRYPTO_MAC_SIZE) { |
226 | 0 | return -1; |
227 | 0 | } |
228 | | |
229 | 30.8k | return 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + len; |
230 | 30.8k | } |
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 | 6.54k | { |
245 | 6.54k | if (CRYPTO_NONCE_SIZE + SIZE_IPPORT + SEND_BASE * 2 + length > max_packet_length || length == 0) { |
246 | 0 | return -1; |
247 | 0 | } |
248 | | |
249 | 6.54k | const uint16_t step1_size = SIZE_IPPORT + length; |
250 | 6.54k | VLA(uint8_t, step1, step1_size); |
251 | | |
252 | 6.54k | ipport_pack(step1, dest); |
253 | 6.54k | memcpy(step1 + SIZE_IPPORT, data, length); |
254 | | |
255 | 6.54k | uint8_t nonce[CRYPTO_NONCE_SIZE]; |
256 | 6.54k | random_nonce(rng, nonce); |
257 | | |
258 | 6.54k | const uint16_t step2_size = SIZE_IPPORT + SEND_BASE + length; |
259 | 6.54k | VLA(uint8_t, step2, step2_size); |
260 | 6.54k | ipport_pack(step2, &path->ip_port3); |
261 | 6.54k | memcpy(step2 + SIZE_IPPORT, path->public_key3, CRYPTO_PUBLIC_KEY_SIZE); |
262 | | |
263 | 6.54k | int len = encrypt_data_symmetric(mem, path->shared_key3, nonce, step1, step1_size, |
264 | 6.54k | step2 + SIZE_IPPORT + CRYPTO_PUBLIC_KEY_SIZE); |
265 | | |
266 | 6.54k | if (len != SIZE_IPPORT + length + CRYPTO_MAC_SIZE) { |
267 | 0 | return -1; |
268 | 0 | } |
269 | | |
270 | 6.54k | ipport_pack(packet + CRYPTO_NONCE_SIZE, &path->ip_port2); |
271 | 6.54k | memcpy(packet + CRYPTO_NONCE_SIZE + SIZE_IPPORT, path->public_key2, CRYPTO_PUBLIC_KEY_SIZE); |
272 | 6.54k | len = encrypt_data_symmetric(mem, path->shared_key2, nonce, step2, step2_size, |
273 | 6.54k | packet + CRYPTO_NONCE_SIZE + SIZE_IPPORT + CRYPTO_PUBLIC_KEY_SIZE); |
274 | | |
275 | 6.54k | if (len != SIZE_IPPORT + SEND_BASE + length + CRYPTO_MAC_SIZE) { |
276 | 0 | return -1; |
277 | 0 | } |
278 | | |
279 | 6.54k | memcpy(packet, nonce, CRYPTO_NONCE_SIZE); |
280 | | |
281 | 6.54k | return CRYPTO_NONCE_SIZE + SIZE_IPPORT + CRYPTO_PUBLIC_KEY_SIZE + len; |
282 | 6.54k | } |
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 | 36.4k | { |
294 | 36.4k | if (length > ONION_RESPONSE_MAX_DATA_SIZE || length == 0) { |
295 | 0 | return -1; |
296 | 0 | } |
297 | | |
298 | 36.4k | const uint16_t packet_size = 1 + RETURN_3 + length; |
299 | 36.4k | VLA(uint8_t, packet, packet_size); |
300 | 36.4k | packet[0] = NET_PACKET_ONION_RECV_3; |
301 | 36.4k | memcpy(packet + 1, ret, RETURN_3); |
302 | 36.4k | memcpy(packet + 1 + RETURN_3, data, length); |
303 | | |
304 | 36.4k | if ((uint16_t)sendpacket(net, dest, packet, packet_size) != packet_size) { |
305 | 1 | return -1; |
306 | 1 | } |
307 | | |
308 | 36.4k | Ip_Ntoa ip_str; |
309 | 36.4k | LOGGER_TRACE(log, "forwarded onion RECV_3 to %s:%d (%02x in %02x, %d bytes)", |
310 | 36.4k | net_ip_ntoa(&dest->ip, &ip_str), net_ntohs(dest->port), data[0], packet[0], packet_size); |
311 | 36.4k | return 0; |
312 | 36.4k | } |
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 | 30.6k | { |
316 | 30.6k | Onion *onion = (Onion *)object; |
317 | | |
318 | 30.6k | 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 | 30.6k | 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 | 30.6k | change_symmetric_key(onion); |
331 | | |
332 | 30.6k | const int nonce_start = 1; |
333 | 30.6k | const int public_key_start = nonce_start + CRYPTO_NONCE_SIZE; |
334 | 30.6k | const int ciphertext_start = public_key_start + CRYPTO_PUBLIC_KEY_SIZE; |
335 | | |
336 | 30.6k | const int ciphertext_length = length - ciphertext_start; |
337 | 30.6k | const int plaintext_length = ciphertext_length - CRYPTO_MAC_SIZE; |
338 | | |
339 | 30.6k | uint8_t plain[ONION_MAX_PACKET_SIZE]; |
340 | 30.6k | const uint8_t *public_key = &packet[public_key_start]; |
341 | 30.6k | const uint8_t *shared_key = shared_key_cache_lookup(onion->shared_keys_1, public_key); |
342 | | |
343 | 30.6k | 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 | 30.6k | const int len = decrypt_data_symmetric( |
351 | 30.6k | onion->mem, shared_key, &packet[nonce_start], &packet[ciphertext_start], ciphertext_length, plain); |
352 | | |
353 | 30.6k | if (len != plaintext_length) { |
354 | 224 | LOGGER_TRACE(onion->log, "decrypt failed: %d != %d", len, plaintext_length); |
355 | 224 | return 1; |
356 | 224 | } |
357 | | |
358 | 30.4k | return onion_send_1(onion, plain, len, source, packet + 1); |
359 | 30.6k | } |
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 | 36.9k | { |
363 | 36.9k | const uint16_t max_len = ONION_MAX_PACKET_SIZE + SIZE_IPPORT - (1 + CRYPTO_NONCE_SIZE + ONION_RETURN_1); |
364 | 36.9k | 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 | 36.9k | if (len <= SIZE_IPPORT + SEND_BASE * 2) { |
370 | 0 | return 1; |
371 | 0 | } |
372 | | |
373 | 36.9k | IP_Port send_to; |
374 | | |
375 | 36.9k | if (ipport_unpack(&send_to, plain, len, false) == -1) { |
376 | 0 | return 1; |
377 | 0 | } |
378 | | |
379 | | /* Avoid warning message when IPv6 is disabled and node address is IPv6. */ |
380 | 36.9k | if (!net_socket_family_compatible(onion->net, &send_to, false)) { |
381 | 0 | return 1; |
382 | 0 | } |
383 | | |
384 | 36.9k | uint8_t ip_port[SIZE_IPPORT]; |
385 | 36.9k | ipport_pack(ip_port, source); |
386 | | |
387 | 36.9k | uint8_t data[ONION_MAX_PACKET_SIZE] = {0}; |
388 | 36.9k | data[0] = NET_PACKET_ONION_SEND_1; |
389 | 36.9k | memcpy(data + 1, nonce, CRYPTO_NONCE_SIZE); |
390 | 36.9k | memcpy(data + 1 + CRYPTO_NONCE_SIZE, plain + SIZE_IPPORT, len - SIZE_IPPORT); |
391 | 36.9k | uint16_t data_len = 1 + CRYPTO_NONCE_SIZE + (len - SIZE_IPPORT); |
392 | 36.9k | uint8_t *ret_part = data + data_len; |
393 | 36.9k | random_nonce(onion->rng, ret_part); |
394 | 36.9k | len = encrypt_data_symmetric(onion->mem, onion->secret_symmetric_key, ret_part, ip_port, SIZE_IPPORT, |
395 | 36.9k | ret_part + CRYPTO_NONCE_SIZE); |
396 | | |
397 | 36.9k | if (len != SIZE_IPPORT + CRYPTO_MAC_SIZE) { |
398 | 0 | return 1; |
399 | 0 | } |
400 | | |
401 | 36.9k | data_len += CRYPTO_NONCE_SIZE + len; |
402 | | |
403 | 36.9k | if ((uint32_t)sendpacket(onion->net, &send_to, data, data_len) != data_len) { |
404 | 0 | return 1; |
405 | 0 | } |
406 | | |
407 | 36.9k | Ip_Ntoa ip_str; |
408 | 36.9k | LOGGER_TRACE(onion->log, "forwarded onion packet to %s:%d, level 1 (%02x in %02x, %d bytes)", |
409 | 36.9k | net_ip_ntoa(&send_to.ip, &ip_str), net_ntohs(send_to.port), plain[0], data[0], data_len); |
410 | 36.9k | return 0; |
411 | 36.9k | } |
412 | | |
413 | | static int handle_send_1(void *_Nonnull object, const IP_Port *_Nonnull source, const uint8_t *_Nonnull packet, uint16_t length, void *_Nonnull userdata) |
414 | 36.8k | { |
415 | 36.8k | Onion *onion = (Onion *)object; |
416 | | |
417 | 36.8k | if (length > ONION_MAX_PACKET_SIZE) { |
418 | 0 | return 1; |
419 | 0 | } |
420 | | |
421 | 36.8k | if (length <= 1 + SEND_2) { |
422 | 0 | return 1; |
423 | 0 | } |
424 | | |
425 | 36.8k | change_symmetric_key(onion); |
426 | | |
427 | 36.8k | uint8_t plain[ONION_MAX_PACKET_SIZE]; |
428 | 36.8k | const uint8_t *public_key = packet + 1 + CRYPTO_NONCE_SIZE; |
429 | 36.8k | const uint8_t *shared_key = shared_key_cache_lookup(onion->shared_keys_2, public_key); |
430 | | |
431 | 36.8k | if (shared_key == nullptr) { |
432 | | /* Error looking up/deriving the shared key */ |
433 | 0 | return 1; |
434 | 0 | } |
435 | | |
436 | 36.8k | int len = decrypt_data_symmetric(onion->mem, shared_key, packet + 1, packet + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE, |
437 | 36.8k | length - (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + RETURN_1), plain); |
438 | | |
439 | 36.8k | if (len != length - (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + RETURN_1 + CRYPTO_MAC_SIZE)) { |
440 | 128 | return 1; |
441 | 128 | } |
442 | | |
443 | 36.7k | IP_Port send_to; |
444 | | |
445 | 36.7k | if (ipport_unpack(&send_to, plain, len, false) == -1) { |
446 | 0 | return 1; |
447 | 0 | } |
448 | | |
449 | | /* Avoid warning message when IPv6 is disabled and node address is IPv6. */ |
450 | 36.7k | if (!net_socket_family_compatible(onion->net, &send_to, false)) { |
451 | 0 | return 1; |
452 | 0 | } |
453 | | |
454 | 36.7k | uint8_t data[ONION_MAX_PACKET_SIZE] = {0}; |
455 | 36.7k | data[0] = NET_PACKET_ONION_SEND_2; |
456 | 36.7k | memcpy(data + 1, packet + 1, CRYPTO_NONCE_SIZE); |
457 | 36.7k | memcpy(data + 1 + CRYPTO_NONCE_SIZE, plain + SIZE_IPPORT, len - SIZE_IPPORT); |
458 | 36.7k | uint16_t data_len = 1 + CRYPTO_NONCE_SIZE + (len - SIZE_IPPORT); |
459 | 36.7k | uint8_t *ret_part = data + data_len; |
460 | 36.7k | random_nonce(onion->rng, ret_part); |
461 | 36.7k | uint8_t ret_data[RETURN_1 + SIZE_IPPORT]; |
462 | 36.7k | ipport_pack(ret_data, source); |
463 | 36.7k | memcpy(ret_data + SIZE_IPPORT, packet + (length - RETURN_1), RETURN_1); |
464 | 36.7k | len = encrypt_data_symmetric(onion->mem, onion->secret_symmetric_key, ret_part, ret_data, sizeof(ret_data), |
465 | 36.7k | ret_part + CRYPTO_NONCE_SIZE); |
466 | | |
467 | 36.7k | if (len != RETURN_2 - CRYPTO_NONCE_SIZE) { |
468 | 0 | return 1; |
469 | 0 | } |
470 | | |
471 | 36.7k | data_len += CRYPTO_NONCE_SIZE + len; |
472 | | |
473 | 36.7k | if ((uint32_t)sendpacket(onion->net, &send_to, data, data_len) != data_len) { |
474 | 0 | return 1; |
475 | 0 | } |
476 | | |
477 | 36.7k | Ip_Ntoa ip_str; |
478 | 36.7k | LOGGER_TRACE(onion->log, "forwarded onion packet to %s:%d, level 2 (%02x in %02x, %d bytes)", |
479 | 36.7k | net_ip_ntoa(&send_to.ip, &ip_str), net_ntohs(send_to.port), packet[0], data[0], data_len); |
480 | 36.7k | return 0; |
481 | 36.7k | } |
482 | | |
483 | | static int handle_send_2(void *_Nonnull object, const IP_Port *_Nonnull source, const uint8_t *_Nonnull packet, uint16_t length, void *_Nonnull userdata) |
484 | 36.7k | { |
485 | 36.7k | Onion *onion = (Onion *)object; |
486 | | |
487 | 36.7k | if (length > ONION_MAX_PACKET_SIZE) { |
488 | 0 | return 1; |
489 | 0 | } |
490 | | |
491 | 36.7k | if (length <= 1 + SEND_3) { |
492 | 0 | return 1; |
493 | 0 | } |
494 | | |
495 | 36.7k | change_symmetric_key(onion); |
496 | | |
497 | 36.7k | uint8_t plain[ONION_MAX_PACKET_SIZE]; |
498 | 36.7k | const uint8_t *public_key = packet + 1 + CRYPTO_NONCE_SIZE; |
499 | 36.7k | const uint8_t *shared_key = shared_key_cache_lookup(onion->shared_keys_3, public_key); |
500 | | |
501 | 36.7k | if (shared_key == nullptr) { |
502 | | /* Error looking up/deriving the shared key */ |
503 | 0 | return 1; |
504 | 0 | } |
505 | | |
506 | 36.7k | int len = decrypt_data_symmetric(onion->mem, shared_key, packet + 1, packet + 1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE, |
507 | 36.7k | length - (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + RETURN_2), plain); |
508 | | |
509 | 36.7k | if (len != length - (1 + CRYPTO_NONCE_SIZE + CRYPTO_PUBLIC_KEY_SIZE + RETURN_2 + CRYPTO_MAC_SIZE)) { |
510 | 43 | return 1; |
511 | 43 | } |
512 | | |
513 | 36.6k | assert(len > SIZE_IPPORT); |
514 | | |
515 | 36.6k | const uint8_t packet_id = plain[SIZE_IPPORT]; |
516 | | |
517 | 36.6k | if (packet_id != NET_PACKET_ANNOUNCE_REQUEST && packet_id != NET_PACKET_ANNOUNCE_REQUEST_OLD && |
518 | 36.6k | packet_id != NET_PACKET_ONION_DATA_REQUEST) { |
519 | 0 | return 1; |
520 | 0 | } |
521 | | |
522 | 36.6k | IP_Port send_to; |
523 | | |
524 | 36.6k | if (ipport_unpack(&send_to, plain, len, false) == -1) { |
525 | 0 | return 1; |
526 | 0 | } |
527 | | |
528 | | /* Avoid warning message when IPv6 is disabled and node address is IPv6. */ |
529 | 36.6k | if (!net_socket_family_compatible(onion->net, &send_to, false)) { |
530 | 0 | return 1; |
531 | 0 | } |
532 | | |
533 | 36.6k | uint8_t data[ONION_MAX_PACKET_SIZE] = {0}; |
534 | 36.6k | memcpy(data, plain + SIZE_IPPORT, len - SIZE_IPPORT); |
535 | 36.6k | uint16_t data_len = len - SIZE_IPPORT; |
536 | 36.6k | uint8_t *ret_part = data + (len - SIZE_IPPORT); |
537 | 36.6k | random_nonce(onion->rng, ret_part); |
538 | 36.6k | uint8_t ret_data[RETURN_2 + SIZE_IPPORT]; |
539 | 36.6k | ipport_pack(ret_data, source); |
540 | 36.6k | memcpy(ret_data + SIZE_IPPORT, packet + (length - RETURN_2), RETURN_2); |
541 | 36.6k | len = encrypt_data_symmetric(onion->mem, onion->secret_symmetric_key, ret_part, ret_data, sizeof(ret_data), |
542 | 36.6k | ret_part + CRYPTO_NONCE_SIZE); |
543 | | |
544 | 36.6k | if (len != RETURN_3 - CRYPTO_NONCE_SIZE) { |
545 | 0 | return 1; |
546 | 0 | } |
547 | | |
548 | 36.6k | data_len += RETURN_3; |
549 | | |
550 | 36.6k | if ((uint32_t)sendpacket(onion->net, &send_to, data, data_len) != data_len) { |
551 | 0 | return 1; |
552 | 0 | } |
553 | | |
554 | 36.6k | Ip_Ntoa ip_str; |
555 | 36.6k | LOGGER_TRACE(onion->log, "forwarded onion packet to %s:%d, level 3 (%02x in %02x, %d bytes)", |
556 | 36.6k | net_ip_ntoa(&send_to.ip, &ip_str), net_ntohs(send_to.port), packet[0], data[0], data_len); |
557 | 36.6k | return 0; |
558 | 36.6k | } |
559 | | |
560 | | static int handle_recv_3(void *_Nonnull object, const IP_Port *_Nonnull source, const uint8_t *_Nonnull packet, uint16_t length, void *_Nonnull userdata) |
561 | 36.3k | { |
562 | 36.3k | Onion *onion = (Onion *)object; |
563 | | |
564 | 36.3k | if (length > ONION_MAX_PACKET_SIZE) { |
565 | 0 | return 1; |
566 | 0 | } |
567 | | |
568 | 36.3k | if (length <= 1 + RETURN_3) { |
569 | 0 | return 1; |
570 | 0 | } |
571 | | |
572 | 36.3k | const uint8_t packet_id = packet[1 + RETURN_3]; |
573 | | |
574 | 36.3k | if (packet_id != NET_PACKET_ANNOUNCE_RESPONSE && packet_id != NET_PACKET_ANNOUNCE_RESPONSE_OLD && |
575 | 36.3k | packet_id != NET_PACKET_ONION_DATA_RESPONSE) { |
576 | 0 | return 1; |
577 | 0 | } |
578 | | |
579 | 36.3k | change_symmetric_key(onion); |
580 | | |
581 | 36.3k | uint8_t plain[SIZE_IPPORT + RETURN_2]; |
582 | 36.3k | const int len = decrypt_data_symmetric(onion->mem, onion->secret_symmetric_key, packet + 1, packet + 1 + CRYPTO_NONCE_SIZE, |
583 | 36.3k | SIZE_IPPORT + RETURN_2 + CRYPTO_MAC_SIZE, plain); |
584 | | |
585 | 36.3k | if ((uint32_t)len != sizeof(plain)) { |
586 | 3 | return 1; |
587 | 3 | } |
588 | | |
589 | 36.3k | IP_Port send_to; |
590 | | |
591 | 36.3k | if (ipport_unpack(&send_to, plain, len, false) == -1) { |
592 | 0 | LOGGER_DEBUG(onion->log, "failed to unpack IP/Port"); |
593 | 0 | return 1; |
594 | 0 | } |
595 | | |
596 | 36.3k | uint8_t data[ONION_MAX_PACKET_SIZE] = {0}; |
597 | 36.3k | data[0] = NET_PACKET_ONION_RECV_2; |
598 | 36.3k | memcpy(data + 1, plain + SIZE_IPPORT, RETURN_2); |
599 | 36.3k | memcpy(data + 1 + RETURN_2, packet + 1 + RETURN_3, length - (1 + RETURN_3)); |
600 | 36.3k | const uint16_t data_len = 1 + RETURN_2 + (length - (1 + RETURN_3)); |
601 | | |
602 | 36.3k | if ((uint32_t)sendpacket(onion->net, &send_to, data, data_len) != data_len) { |
603 | 0 | return 1; |
604 | 0 | } |
605 | | |
606 | 36.3k | Ip_Ntoa ip_str; |
607 | 36.3k | LOGGER_TRACE(onion->log, "forwarded onion RECV_2 to %s:%d (%02x in %02x, %d bytes)", |
608 | 36.3k | net_ip_ntoa(&send_to.ip, &ip_str), net_ntohs(send_to.port), packet[0], data[0], data_len); |
609 | 36.3k | return 0; |
610 | 36.3k | } |
611 | | |
612 | | static int handle_recv_2(void *_Nonnull object, const IP_Port *_Nonnull source, const uint8_t *_Nonnull packet, uint16_t length, void *_Nonnull userdata) |
613 | 36.3k | { |
614 | 36.3k | Onion *onion = (Onion *)object; |
615 | | |
616 | 36.3k | if (length > ONION_MAX_PACKET_SIZE) { |
617 | 0 | return 1; |
618 | 0 | } |
619 | | |
620 | 36.3k | if (length <= 1 + RETURN_2) { |
621 | 0 | return 1; |
622 | 0 | } |
623 | | |
624 | 36.3k | const uint8_t packet_id = packet[1 + RETURN_2]; |
625 | | |
626 | 36.3k | if (packet_id != NET_PACKET_ANNOUNCE_RESPONSE && packet_id != NET_PACKET_ANNOUNCE_RESPONSE_OLD && |
627 | 36.3k | packet_id != NET_PACKET_ONION_DATA_RESPONSE) { |
628 | 0 | return 1; |
629 | 0 | } |
630 | | |
631 | 36.3k | change_symmetric_key(onion); |
632 | | |
633 | 36.3k | uint8_t plain[SIZE_IPPORT + RETURN_1]; |
634 | 36.3k | const int len = decrypt_data_symmetric(onion->mem, onion->secret_symmetric_key, packet + 1, packet + 1 + CRYPTO_NONCE_SIZE, |
635 | 36.3k | SIZE_IPPORT + RETURN_1 + CRYPTO_MAC_SIZE, plain); |
636 | | |
637 | 36.3k | if ((uint32_t)len != sizeof(plain)) { |
638 | 1 | return 1; |
639 | 1 | } |
640 | | |
641 | 36.3k | IP_Port send_to; |
642 | | |
643 | 36.3k | if (ipport_unpack(&send_to, plain, len, false) == -1) { |
644 | 0 | return 1; |
645 | 0 | } |
646 | | |
647 | 36.3k | uint8_t data[ONION_MAX_PACKET_SIZE] = {0}; |
648 | 36.3k | data[0] = NET_PACKET_ONION_RECV_1; |
649 | 36.3k | memcpy(data + 1, plain + SIZE_IPPORT, RETURN_1); |
650 | 36.3k | memcpy(data + 1 + RETURN_1, packet + 1 + RETURN_2, length - (1 + RETURN_2)); |
651 | 36.3k | const uint16_t data_len = 1 + RETURN_1 + (length - (1 + RETURN_2)); |
652 | | |
653 | 36.3k | if ((uint32_t)sendpacket(onion->net, &send_to, data, data_len) != data_len) { |
654 | 0 | return 1; |
655 | 0 | } |
656 | | |
657 | 36.3k | Ip_Ntoa ip_str; |
658 | 36.3k | LOGGER_TRACE(onion->log, "forwarded onion RECV_1 to %s:%d (%02x in %02x, %d bytes)", |
659 | 36.3k | net_ip_ntoa(&send_to.ip, &ip_str), net_ntohs(send_to.port), packet[0], data[0], data_len); |
660 | 36.3k | return 0; |
661 | 36.3k | } |
662 | | |
663 | | static int handle_recv_1(void *_Nonnull object, const IP_Port *_Nonnull source, const uint8_t *_Nonnull packet, uint16_t length, void *_Nonnull userdata) |
664 | 36.2k | { |
665 | 36.2k | Onion *onion = (Onion *)object; |
666 | | |
667 | 36.2k | if (length > ONION_MAX_PACKET_SIZE) { |
668 | 0 | return 1; |
669 | 0 | } |
670 | | |
671 | 36.2k | if (length <= 1 + RETURN_1) { |
672 | 0 | return 1; |
673 | 0 | } |
674 | | |
675 | 36.2k | const uint8_t packet_id = packet[1 + RETURN_1]; |
676 | | |
677 | 36.2k | if (packet_id != NET_PACKET_ANNOUNCE_RESPONSE && packet_id != NET_PACKET_ANNOUNCE_RESPONSE_OLD && |
678 | 36.2k | packet_id != NET_PACKET_ONION_DATA_RESPONSE) { |
679 | 0 | return 1; |
680 | 0 | } |
681 | | |
682 | 36.2k | change_symmetric_key(onion); |
683 | | |
684 | 36.2k | uint8_t plain[SIZE_IPPORT]; |
685 | 36.2k | const int len = decrypt_data_symmetric(onion->mem, onion->secret_symmetric_key, packet + 1, packet + 1 + CRYPTO_NONCE_SIZE, |
686 | 36.2k | SIZE_IPPORT + CRYPTO_MAC_SIZE, plain); |
687 | | |
688 | 36.2k | if ((uint32_t)len != SIZE_IPPORT) { |
689 | 1 | return 1; |
690 | 1 | } |
691 | | |
692 | 36.2k | IP_Port send_to; |
693 | | |
694 | 36.2k | if (ipport_unpack(&send_to, plain, len, true) == -1) { |
695 | 0 | LOGGER_DEBUG(onion->log, "failed to unpack IP/Port"); |
696 | 0 | return 1; |
697 | 0 | } |
698 | | |
699 | 36.2k | const uint16_t data_len = length - (1 + RETURN_1); |
700 | | |
701 | 36.2k | if (onion->recv_1_function != nullptr && |
702 | 36.2k | !net_family_is_ipv4(send_to.ip.family) && |
703 | 36.2k | !net_family_is_ipv6(send_to.ip.family)) { |
704 | 6.54k | return onion->recv_1_function(onion->callback_object, &send_to, packet + (1 + RETURN_1), data_len); |
705 | 6.54k | } |
706 | | |
707 | 29.6k | if ((uint32_t)sendpacket(onion->net, &send_to, packet + (1 + RETURN_1), data_len) != data_len) { |
708 | 0 | return 1; |
709 | 0 | } |
710 | | |
711 | 29.6k | return 0; |
712 | 29.6k | } |
713 | | |
714 | | void set_callback_handle_recv_1(Onion *onion, onion_recv_1_cb *function, void *object) |
715 | 8 | { |
716 | 8 | onion->recv_1_function = function; |
717 | 8 | onion->callback_object = object; |
718 | 8 | } |
719 | | |
720 | | Onion *new_onion(const Logger *log, const Memory *mem, const Mono_Time *mono_time, const Random *rng, DHT *dht, Networking_Core *net) |
721 | 1.95k | { |
722 | 1.95k | if (dht == nullptr) { |
723 | 0 | return nullptr; |
724 | 0 | } |
725 | | |
726 | 1.95k | Onion *onion = (Onion *)mem_alloc(mem, sizeof(Onion)); |
727 | | |
728 | 1.95k | if (onion == nullptr) { |
729 | 12 | return nullptr; |
730 | 12 | } |
731 | | |
732 | 1.94k | onion->log = log; |
733 | 1.94k | onion->dht = dht; |
734 | 1.94k | onion->net = net; |
735 | 1.94k | onion->mono_time = mono_time; |
736 | 1.94k | onion->rng = rng; |
737 | 1.94k | onion->mem = mem; |
738 | 1.94k | new_symmetric_key(rng, onion->secret_symmetric_key); |
739 | 1.94k | onion->timestamp = mono_time_get(onion->mono_time); |
740 | | |
741 | 1.94k | const uint8_t *secret_key = dht_get_self_secret_key(dht); |
742 | 1.94k | Shared_Key_Cache *const temp_shared_keys_1 = shared_key_cache_new(log, mono_time, mem, secret_key, KEYS_TIMEOUT, MAX_KEYS_PER_SLOT); |
743 | 1.94k | Shared_Key_Cache *const temp_shared_keys_2 = shared_key_cache_new(log, mono_time, mem, secret_key, KEYS_TIMEOUT, MAX_KEYS_PER_SLOT); |
744 | 1.94k | Shared_Key_Cache *const temp_shared_keys_3 = shared_key_cache_new(log, mono_time, mem, secret_key, KEYS_TIMEOUT, MAX_KEYS_PER_SLOT); |
745 | | |
746 | 1.94k | if (temp_shared_keys_1 == nullptr || temp_shared_keys_2 == nullptr || temp_shared_keys_3 == nullptr) { |
747 | 12 | shared_key_cache_free(temp_shared_keys_3); |
748 | 12 | shared_key_cache_free(temp_shared_keys_2); |
749 | 12 | shared_key_cache_free(temp_shared_keys_1); |
750 | | // cppcheck-suppress mismatchAllocDealloc |
751 | 12 | kill_onion(onion); |
752 | 12 | return nullptr; |
753 | 12 | } |
754 | | |
755 | 1.93k | onion->shared_keys_1 = temp_shared_keys_1; |
756 | 1.93k | onion->shared_keys_2 = temp_shared_keys_2; |
757 | 1.93k | onion->shared_keys_3 = temp_shared_keys_3; |
758 | | |
759 | 1.93k | networking_registerhandler(onion->net, NET_PACKET_ONION_SEND_INITIAL, &handle_send_initial, onion); |
760 | 1.93k | networking_registerhandler(onion->net, NET_PACKET_ONION_SEND_1, &handle_send_1, onion); |
761 | 1.93k | networking_registerhandler(onion->net, NET_PACKET_ONION_SEND_2, &handle_send_2, onion); |
762 | | |
763 | 1.93k | networking_registerhandler(onion->net, NET_PACKET_ONION_RECV_3, &handle_recv_3, onion); |
764 | 1.93k | networking_registerhandler(onion->net, NET_PACKET_ONION_RECV_2, &handle_recv_2, onion); |
765 | 1.93k | networking_registerhandler(onion->net, NET_PACKET_ONION_RECV_1, &handle_recv_1, onion); |
766 | | |
767 | 1.93k | return onion; |
768 | 1.94k | } |
769 | | |
770 | | void kill_onion(Onion *onion) |
771 | 1.94k | { |
772 | 1.94k | if (onion == nullptr) { |
773 | 21 | return; |
774 | 21 | } |
775 | | |
776 | 1.92k | networking_registerhandler(onion->net, NET_PACKET_ONION_SEND_INITIAL, nullptr, nullptr); |
777 | 1.92k | networking_registerhandler(onion->net, NET_PACKET_ONION_SEND_1, nullptr, nullptr); |
778 | 1.92k | networking_registerhandler(onion->net, NET_PACKET_ONION_SEND_2, nullptr, nullptr); |
779 | | |
780 | 1.92k | networking_registerhandler(onion->net, NET_PACKET_ONION_RECV_3, nullptr, nullptr); |
781 | 1.92k | networking_registerhandler(onion->net, NET_PACKET_ONION_RECV_2, nullptr, nullptr); |
782 | 1.92k | networking_registerhandler(onion->net, NET_PACKET_ONION_RECV_1, nullptr, nullptr); |
783 | | |
784 | 1.92k | crypto_memzero(onion->secret_symmetric_key, sizeof(onion->secret_symmetric_key)); |
785 | | |
786 | 1.92k | shared_key_cache_free(onion->shared_keys_1); |
787 | 1.92k | shared_key_cache_free(onion->shared_keys_2); |
788 | 1.92k | shared_key_cache_free(onion->shared_keys_3); |
789 | | |
790 | 1.92k | mem_delete(onion->mem, onion); |
791 | 1.92k | } |