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