diff options
author | Alexandre Erwin Ittner <alexandre@ittner.com.br> | 2014-08-30 16:43:07 -0300 |
---|---|---|
committer | Alexandre Erwin Ittner <alexandre@ittner.com.br> | 2014-08-30 16:43:07 -0300 |
commit | e4f66475d8b47ee3b289fdd75bbbce3230949eed (patch) | |
tree | 0456f8eb0e7a1eb718837249771578b0151dfdaa /docs | |
parent | fa007a3b047812e091ec19f34e9b9ab53a2f47b3 (diff) |
Add support for user avatars in the core protocol
Add a protocol and the APIs to straightforwardly support user avatars
in client applications. The protocol is designed to transfer avatars
in background, between friends only, and minimize network load by
providing a lightweight avatar notification for local cache validation.
Strict safeguards are imposed to avoid damage from non-cooperative or
malicious users and to limit network usage.
The complete documentation is available in docs/Avatars.md and sample
code is available in testing/test_avatars.c.
Code and documentation are released under the GNU GPLv3 or later, as
described in the file COPYING.
Diffstat (limited to 'docs')
-rw-r--r-- | docs/Avatars.md | 658 |
1 files changed, 658 insertions, 0 deletions
diff --git a/docs/Avatars.md b/docs/Avatars.md new file mode 100644 index 00000000..bd4046d4 --- /dev/null +++ b/docs/Avatars.md | |||
@@ -0,0 +1,658 @@ | |||
1 | # User avatars in Tox | ||
2 | |||
3 | |||
4 | |||
5 | ## Introduction and rationale | ||
6 | |||
7 | User avatars are small icons or images used to identify users in the friend | ||
8 | list; they exists in virtually all VoIP and IM protocols and provide an easy | ||
9 | way to an user identify another in the friend list. | ||
10 | |||
11 | This document describes the implementation of avatars in the Tox protocol, | ||
12 | according to the following design considerations: | ||
13 | |||
14 | - Avatars are handled as private information, ie., only exchanged over | ||
15 | Tox encrypted channels among previously authenticated friends; | ||
16 | |||
17 | - The library treats all images as blobs and does not interpret or | ||
18 | understands image formats, only ensures that the avatar data sent by | ||
19 | an user is correctly received by the other. The client application is | ||
20 | responsible for validating, decoding, resizing, and presenting the | ||
21 | image to the user. | ||
22 | |||
23 | - There is a strict limit of 16 KiB to the avatar raw data size -- this | ||
24 | seems suitable for practical use as, for example, the raw data of an | ||
25 | uncompressed 64 x 64 pixels 24 bpp RGB bitmap is 12288 bytes long; the | ||
26 | data limit provides enough space for larger bitmaps if the usual | ||
27 | compressed formats are used. | ||
28 | |||
29 | **Notice:** As designed, this limit can be changed in the future without | ||
30 | breaking the protocol compatibility, but clients using the original | ||
31 | limit will reject larger avatars; | ||
32 | |||
33 | - The protocol MUST provide means to allow caching and avoid unnecessary | ||
34 | data transfers; | ||
35 | |||
36 | - Avatars are transfered between clients in a background operation; | ||
37 | |||
38 | - Avatars are served in a "best effort" basis, without breaking clients | ||
39 | who do not support them; | ||
40 | |||
41 | - The protocol MUST resist to malicious users; | ||
42 | |||
43 | - The protocol MUST work with both UDP and TCP networks. | ||
44 | |||
45 | |||
46 | The Single Tox Standard Draft v.0.1.0 recommends implementing avatars as | ||
47 | a purely client-side feature through a procedure that can be summarized as | ||
48 | sending a specially named file as a file transfer request and accepting | ||
49 | it silently. This procedure can be improved to provide the previously stated | ||
50 | design considerations, but this requires a higher integration with the core | ||
51 | protocol. Moving this feature to the core protocol also: | ||
52 | |||
53 | - Provides a simpler and cleaner interfaces for client applications; | ||
54 | |||
55 | - Hides protocol complexities from the client; | ||
56 | |||
57 | - Avoids code duplication and ad-hoc protocols in the clients; | ||
58 | |||
59 | - Avoids incompatibility between client implementations; | ||
60 | |||
61 | - Allows important optimizations such as lightweight notification of | ||
62 | removed and updated avatars; | ||
63 | |||
64 | - Plays well with cache schemes; | ||
65 | |||
66 | - Makes avatar transfer an essentially background operation. | ||
67 | |||
68 | |||
69 | |||
70 | |||
71 | |||
72 | |||
73 | ## High level description | ||
74 | |||
75 | The avatar exchange is implemented with the following new elements in the | ||
76 | Tox protocol. This is a very high level description and the usage patterns | ||
77 | expected from client applications are described in Section "Using Avatars | ||
78 | in Client Applications" and a low level protocol description is available | ||
79 | in Section "Internal Protocol Description". | ||
80 | |||
81 | - **Avatar Information Notifications** are events which may be sent by | ||
82 | an user to another anytime, but are usually sent after one of them | ||
83 | connects to the network, changes his avatar, or in reply to an **avatar | ||
84 | information request**. They are delivered by a very lightweight message | ||
85 | but with information enough to allow an user to validate or discard an | ||
86 | avatar from the local cache and decide if is interesting to request the | ||
87 | avatar data from the peer. | ||
88 | |||
89 | This event contain two data fields: (1) the image format and (2) the | ||
90 | cryptographic hash of the actual image data. Image format may be NONE | ||
91 | (for users who have no avatar or removed their avatars), JPEG, PNG, or | ||
92 | GIF. The cryptographic hash is intended to be compared with the hash o | ||
93 | the currently cached avatar (if any) and check if it stills up to date. | ||
94 | |||
95 | - **Avatar Information Requests** are very lightweight messages sent by an | ||
96 | user asking for an **avatar information notification**. They may be sent | ||
97 | as part of the login process or when the client thinks the currently | ||
98 | cached avatar is outdated. The receiver may or may not answer to this | ||
99 | request. This message contains no data fields; | ||
100 | |||
101 | - An **Avatar Data Request** is sent by an user asking another for his | ||
102 | complete avatar data. It is sent only when the requesting user decides | ||
103 | the avatar do not exists in the local cache or is outdated. The receiver | ||
104 | may or may not answer to this request. This message contains no data | ||
105 | fields. | ||
106 | |||
107 | - An **Avatar Data Notification** is an event signaling the client that | ||
108 | the complete avatar image data of another user is available. The actual | ||
109 | data transfer is implemented using several data and control messages, | ||
110 | but the details are hidden from the client applications. This event can | ||
111 | only arrive in reply to an **avatar data request**. | ||
112 | |||
113 | This event contains three data fields: (1) the image format, (2) the | ||
114 | cryptographic hash of the image data, and (3) the raw image data. If the | ||
115 | image format is NONE (i.e. no avatar) the hash is zeroed and the image | ||
116 | data is empty. The raw image data is locally validated and ensured to | ||
117 | match the hash (the event is **not** triggered otherwise). | ||
118 | |||
119 | |||
120 | |||
121 | |||
122 | |||
123 | ## API | ||
124 | |||
125 | To implement this feature, the following public symbols were added. The | ||
126 | complete API documentation is available in `tox.h`. | ||
127 | |||
128 | |||
129 | ``` | ||
130 | #define TOX_MAX_AVATAR_DATA_LENGTH 16384 | ||
131 | #define TOX_AVATAR_HASH_LENGTH 32 | ||
132 | |||
133 | |||
134 | /* Data formats for user avatar images */ | ||
135 | typedef enum { | ||
136 | TOX_AVATARFORMAT_NONE, | ||
137 | TOX_AVATARFORMAT_JPEG, | ||
138 | TOX_AVATARFORMAT_PNG, | ||
139 | TOX_AVATARFORMAT_GIF | ||
140 | } | ||
141 | TOX_AVATARFORMAT; | ||
142 | |||
143 | |||
144 | |||
145 | /* Set the user avatar image data. */ | ||
146 | int tox_set_avatar(Tox *tox, uint8_t format, const uint8_t *data, uint32_t length); | ||
147 | |||
148 | /* Get avatar data from the current user. */ | ||
149 | int tox_get_self_avatar(const Tox *tox, uint8_t *format, uint8_t *buf, uint32_t *length, uint32_t maxlen, uint8_t *hash); | ||
150 | |||
151 | /* Generates a cryptographic hash of the given avatar data. */ | ||
152 | int tox_avatar_hash(const Tox *tox, uint8_t *hash, const uint8_t *data, const uint32_t datalen); | ||
153 | |||
154 | /* Request avatar information from a friend. */ | ||
155 | int tox_request_avatar_info(const Tox *tox, const int32_t friendnumber); | ||
156 | |||
157 | /* Send an unrequested avatar information to a friend. */ | ||
158 | int tox_send_avatar_info(Tox *tox, const int32_t friendnumber); | ||
159 | |||
160 | /* Request the avatar data from a friend. */ | ||
161 | int tox_request_avatar_data(const Tox *tox, const int32_t friendnumber); | ||
162 | |||
163 | /* Set the callback function for avatar data. */ | ||
164 | void tox_callback_avatar_info(Tox *tox, void (*function)(Tox *tox, int32_t, uint8_t, uint8_t*, void *), void *userdata); | ||
165 | |||
166 | /* Set the callback function for avatar data. */ | ||
167 | void tox_callback_avatar_data(Tox *tox, void (*function)(Tox *tox, int32_t, uint8_t, uint8_t*, uint8_t*, uint32_t, void *), void *userdata); | ||
168 | ``` | ||
169 | |||
170 | |||
171 | |||
172 | |||
173 | ## Using Avatars in Client Applications | ||
174 | |||
175 | |||
176 | ### General recommendations | ||
177 | |||
178 | - Clients MUST NOT imply the availability of avatars in other users. | ||
179 | Avatars are an optional feature and not all users and clients may | ||
180 | support them; | ||
181 | |||
182 | - Clients MUST NOT block waiting for avatar information and avatar data | ||
183 | packets; | ||
184 | |||
185 | - Clients MUST treat avatar data as insecure and potentially malicious; | ||
186 | For example, users may accidentally use corrupted images as avatars, | ||
187 | a malicious user may send a specially crafted image to exploit a know | ||
188 | vulnerability in an image decoding library, etc. It is recommended to | ||
189 | handle the avatar image data in the same way as an image downloaded | ||
190 | from an unknown Internet source; | ||
191 | |||
192 | - The peers MUST NOT assume any coupling between the operations of | ||
193 | receiving an avatar information packet, sending unrequested avatar | ||
194 | information packets, requesting avatar data, or receiving avatar data. | ||
195 | |||
196 | For example, the following situations are valid: | ||
197 | |||
198 | * A text-mode client may send avatars to other users, but never | ||
199 | request them; | ||
200 | |||
201 | * A client may not understand a particular image format and ignore | ||
202 | avatars using it, but request and handle other formats; | ||
203 | |||
204 | - Clients SHOULD implement a local cache of avatars and do not request | ||
205 | avatar data from other peers unless necessary; | ||
206 | |||
207 | - When an avatar information is received, the client should delete the | ||
208 | avatar if the new avatar format is NONE or compare the hash received | ||
209 | from the peer with the hash of the currently cached avatar. If they | ||
210 | differ, send an avatar data request; | ||
211 | |||
212 | - If the cached avatar is older than a given threshold, the client may | ||
213 | also send an avatar info request to that friend once he is online and | ||
214 | mark the avatar as updated *before* any avatar information is received | ||
215 | (to not spam the peer with such requests); | ||
216 | |||
217 | - When an avatar data notification is received, the client must update | ||
218 | the cached avatar with the new one; | ||
219 | |||
220 | - Clients should resize or crop the image to the way it better adapts | ||
221 | to the client user interface; | ||
222 | |||
223 | - If the user already have an avatar defined in the client configuration, | ||
224 | it must be set before connecting to the network to avoid spurious avatar | ||
225 | change notifications and unnecessary data transfers. | ||
226 | |||
227 | - If no avatar data is available for a given friend, the client should | ||
228 | show a placeholder image. | ||
229 | |||
230 | |||
231 | |||
232 | ### Interoperability and sharing avatars among different clients | ||
233 | |||
234 | **This section is a tentative recommendation of how clients should store | ||
235 | avatars to ensure local interoperability and should be revised if this | ||
236 | code is accepted into Tox core.** | ||
237 | |||
238 | It is desirable that the user avatar and the cached friends avatars could be | ||
239 | shared among different Tox clients in the same system, in the spirit of the | ||
240 | proposed Single Tox Standard. This not only makes switching from one client | ||
241 | to another easier, but also minimizes the need of data transfers, as avatars | ||
242 | already downloaded by other clients can be reused. | ||
243 | |||
244 | Given the Tox data directory described in STS Draft v0.1.0: | ||
245 | |||
246 | - The user avatar is stored in a file named "avatar.ext", where "ext" is | ||
247 | "jpg", "png", or "gif", according to the image format. Clients should | ||
248 | keep just one of these files, with the data of the last avatar set by | ||
249 | the user. If the user have no avatar, no such files should be kept in | ||
250 | the data directory; | ||
251 | |||
252 | - Friends avatars are stored in a directory called "avatars" and named | ||
253 | as "xxxxx.ext", where "xxxxx" is the complete client id encoded as an | ||
254 | uppercase hexadecimal string and "ext" is "jpg", "png", or "gif", | ||
255 | according to the image format. Clients should keep just one of these | ||
256 | files per friend, with the data received from the last avatar data | ||
257 | notification. No file should be kept for an user who have no avatar. | ||
258 | |||
259 | **To be discussed:** User keys are usually presented in Tox clients as | ||
260 | upper case strings, but lower case file names are more usual. | ||
261 | |||
262 | |||
263 | Example for Linux and other Unix systems, assuming an user called "gildor": | ||
264 | |||
265 | Tox data directory: /home/gildor/.config/tox/ | ||
266 | Tox data file: /home/gildor/.config/tox/data | ||
267 | Gildor's avatar: /home/gildor/.config/tox/avatar.jpg | ||
268 | Avatar data dir: /home/gildor/.config/tox/avatars/ | ||
269 | Elrond's avatar: /home/gildor/.config/tox/avatars/43656C65627269616E20646F6E277420546F782E426164206D656D6F72696573.jpg | ||
270 | Elladan's avatar: /home/gildor/.config/tox/avatars/49486174655768656E48756D616E735468696E6B49416D4D7942726F74686572.gif | ||
271 | Elrohir's avatar /home/gildor/.config/tox/avatars/726568746F7242794D6D41496B6E696854736E616D75486E6568576574614849.jpg | ||
272 | Arwen's avatar: /home/gildor/.config/tox/avatars/53686520746F6F6B20476C6F7266696E64656C277320706C6163652068657265.png | ||
273 | Lindir's avatar: /home/gildor/.config/tox/avatars/417070735772697474656E42794D6F7274616C734C6F6F6B54686553616D652E.gif | ||
274 | |||
275 | This recommendation is partially implemented by "testing/test_avatars.c". | ||
276 | |||
277 | |||
278 | |||
279 | |||
280 | |||
281 | ### Common operations | ||
282 | |||
283 | These are minimal examples of how perform common operations with avatar | ||
284 | functions. For a complete, working, example, see `testing/test_avatars.c`. | ||
285 | |||
286 | |||
287 | #### Setting an avatar for the current user | ||
288 | |||
289 | In this example `load_data_file` is just an hypothetical function that loads | ||
290 | data from a file into the buffer and sets the length accordingly. | ||
291 | |||
292 | uint8_t buf[TOX_MAX_AVATAR_DATA_LENGTH]; | ||
293 | uint32_t len; | ||
294 | |||
295 | if (load_data_file("avatar.png", buf, &len) == 0) | ||
296 | if (tox_set_avatar(tox, TOX_AVATARFORMAT_PNG, buf, len) != 0) | ||
297 | fprintf(stderr, "Failed to set avatar.\n"); | ||
298 | |||
299 | If the user is connected, this function will also notify all connected | ||
300 | friends about the avatar change. | ||
301 | |||
302 | If the user already have an avatar defined in the client configuration, it | ||
303 | must be set before connecting to the network to avoid spurious avatar change | ||
304 | notifications and unnecessary data transfers. | ||
305 | |||
306 | |||
307 | |||
308 | |||
309 | #### Removing the avatar from the current user | ||
310 | |||
311 | To remove an avatar, an application must set it to `TOX_AVATARFORMAT_NONE`. | ||
312 | |||
313 | tox_set_avatar(tox, TOX_AVATARFORMAT_NONE, NULL, 0); | ||
314 | |||
315 | If the user is connected, this function will also notify all connected | ||
316 | friends about the avatar change. | ||
317 | |||
318 | |||
319 | |||
320 | |||
321 | |||
322 | #### Receiving avatar information from friends | ||
323 | |||
324 | All avatar information is passed to a callback function with the prototype: | ||
325 | |||
326 | void function(Tox *tox, int32_t friendnumber, uint8_t format, | ||
327 | uint8_t *hash, uint8_t *data, uint32_t datalen, void *userdata) | ||
328 | |||
329 | As in this example: | ||
330 | |||
331 | static void avatar_info_cb(Tox *tox, int32_t friendnumber, uint8_t format, | ||
332 | uint8_t *hash, void *userdata) | ||
333 | { | ||
334 | printf("Receiving avatar information from friend %d. Format = %d\n", | ||
335 | friendnumber, format); | ||
336 | printf("Data hash: "); | ||
337 | hex_printf(hash, TOX_AVATAR_HASH_LENGTH); /* Hypothetical function */ | ||
338 | printf("\n"); | ||
339 | } | ||
340 | |||
341 | And, somewhere in the Tox initialization calls, set if as the callback to be | ||
342 | triggered when an avatar information event arrives: | ||
343 | |||
344 | tox_callback_avatar_info(tox, avatar_info_cb, NULL); | ||
345 | |||
346 | |||
347 | A typical client will test the currently cached avatar against the hash given | ||
348 | in the avatar information event and, if needed, request the avatar data. | ||
349 | |||
350 | |||
351 | |||
352 | #### Receiving avatar data from friends | ||
353 | |||
354 | Avatar data events are only delivered in reply of avatar data requests which | ||
355 | **should** only be sent after getting the user avatar information (format | ||
356 | and hash) from an avatar information event and checking it against a local | ||
357 | cache. | ||
358 | |||
359 | For this, an application must define an avatar information callback which | ||
360 | checks the local avatar cache and emits an avatar data request if necessary: | ||
361 | |||
362 | static void avatar_info_cb(Tox *tox, int32_t friendnumber, uint8_t format, | ||
363 | uint8_t *hash, void *userdata) | ||
364 | { | ||
365 | printf("Receiving avatar information from friend %d. Format = %d\n", | ||
366 | friendnumber, format); | ||
367 | if (format = TOX_AVATARFORMAT_NONE) { | ||
368 | /* User have no avatar or removed the avatar */ | ||
369 | delete_avatar_from_cache(tox, friendnumber); | ||
370 | } else { | ||
371 | /* Use the received hash to check if the cached avatar is | ||
372 | still updated. */ | ||
373 | if (!is_user_cached_avatar_updated(tox, friendnumber, hash)) { | ||
374 | /* User avatar is outdated, send data request */ | ||
375 | tox_request_avatar_data(tox, friendnumber); | ||
376 | } | ||
377 | } | ||
378 | } | ||
379 | |||
380 | |||
381 | Then define an avatar data callback to store the received data in the local | ||
382 | cache: | ||
383 | |||
384 | static void avatar_data_cb(Tox *tox, int32_t friendnumber, uint8_t format, | ||
385 | uint8_t *hash, uint8_t *data, uint32_t datalen, void *userdata) | ||
386 | { | ||
387 | if (format = TOX_AVATARFORMAT_NONE) { | ||
388 | /* User have no avatar or removed the avatar */ | ||
389 | delete_avatar_from_cache(tox, friendnumber); | ||
390 | } else { | ||
391 | save_avatar_data_to_cache(tox, friendnumber, format, hash, | ||
392 | data, datalen); | ||
393 | } | ||
394 | } | ||
395 | |||
396 | |||
397 | And, finally, register both callbacks somewhere in the Tox initialization | ||
398 | calls: | ||
399 | |||
400 | tox_callback_avatar_info(tox, avatar_info_cb, NULL); | ||
401 | tox_callback_avatar_data(tox, avatar_data_cb, NULL); | ||
402 | |||
403 | |||
404 | In the previous examples, implementation of the functions to check, store | ||
405 | and retrieve data from the cache were omitted for brevity. These functions | ||
406 | will also need to get the friend client ID (public key) from they friend | ||
407 | number and, usually, convert it from a byte string to a hexadecimal | ||
408 | string. A complete, yet more complex, example is available in the file | ||
409 | `testing/test_avatars.c`. | ||
410 | |||
411 | |||
412 | |||
413 | |||
414 | |||
415 | |||
416 | |||
417 | |||
418 | |||
419 | |||
420 | |||
421 | ## Internal Protocol Description | ||
422 | |||
423 | ### New packet types | ||
424 | |||
425 | The avatar transfer protocol adds the following new packet types and ids: | ||
426 | |||
427 | PACKET_ID_AVATAR_INFO_REQ = 52 | ||
428 | PACKET_ID_AVATAR_INFO = 53 | ||
429 | PACKET_ID_AVATAR_DATA_CONTROL = 54 | ||
430 | PACKET_ID_AVATAR_DATA_START = 55 | ||
431 | PACKET_ID_AVATAR_DATA_PUSH = 56 | ||
432 | |||
433 | |||
434 | |||
435 | |||
436 | ### Requesting avatar information | ||
437 | |||
438 | To request avatar information, an user must send a packet of type | ||
439 | `PACKET_ID_AVATAR_INFO_REQ`. This packet have no data fields. Upon | ||
440 | receiving this packet, a client which supports avatars should answer with | ||
441 | a `PACKET_ID_AVATAR_INFO`. The sender must accept that the friend may | ||
442 | not answer at all. | ||
443 | |||
444 | |||
445 | |||
446 | |||
447 | ### Receiving avatar information | ||
448 | |||
449 | Avatar information arrives in a packet of type `PACKET_ID_AVATAR_INFO` with | ||
450 | the following structure: | ||
451 | |||
452 | PACKET_ID_AVATAR_INFO (53) | ||
453 | Packet data size: 33 bytes | ||
454 | [1: uint8_t format][32: uint8_t hash] | ||
455 | |||
456 | Where 'format' is the image data format, one of the following: | ||
457 | |||
458 | 0 = AVATARFORMAT_NONE (no avatar set) | ||
459 | 1 = AVATARFORMAT_JPEG | ||
460 | 2 = AVATARFORMAT_PNG | ||
461 | 3 = AVATARFORMAT_GIF | ||
462 | |||
463 | and 'hash' is the SHA-256 message digest of the avatar data. | ||
464 | |||
465 | This packet may be sent at any time and no previous request is required. | ||
466 | Clients should send this packet upon connection or when a friend | ||
467 | connects, in the same way Tox sends name, status and action information. | ||
468 | |||
469 | |||
470 | |||
471 | |||
472 | |||
473 | ### Requesting avatar data | ||
474 | |||
475 | Transmission of avatar data is a multi-step procedure using three new packet | ||
476 | types. | ||
477 | |||
478 | - Packet `PACKET_ID_AVATAR_DATA_CONTROL` have the format: | ||
479 | |||
480 | PACKET_ID_AVATAR_DATA_CONTROL (54) | ||
481 | Packet data size: 5 bytes | ||
482 | [1: uint8_t op][1: uint32_t bytes_received] | ||
483 | |||
484 | where 'op' is a code signaling both an operation request or a status | ||
485 | return, which semantics are explained bellow. The following values are | ||
486 | defined: | ||
487 | |||
488 | 0 = AVATARDATACONTROL_REQ | ||
489 | 1 = AVATARDATACONTROL_MORE | ||
490 | 2 = AVATARDATACONTROL_ERROR | ||
491 | |||
492 | and 'bytes_received' is the number of bytes already received by the | ||
493 | client when the operation is `MORE` or zero otherwise. | ||
494 | |||
495 | |||
496 | - Packet `PACKET_ID_AVATAR_DATA_START` have the following format: | ||
497 | |||
498 | PACKET_ID_AVATAR_DATA_START (55) | ||
499 | Packet data size: 37 bytes | ||
500 | [1: uint8_t format][32: uint8_t hash][1: uint32_t data_length] | ||
501 | |||
502 | |||
503 | where 'format' is the image format, with the same values accepted for | ||
504 | the field 'format' in packet type `PACKET_ID_AVATAR_INFO`, 'hash' is | ||
505 | the SHA-256 cryptographic hash of the avatar raw data and 'data_length' | ||
506 | is the total number of bytes the raw avatar data. | ||
507 | |||
508 | |||
509 | - Packet `PACKET_ID_AVATAR_DATA_PUSH` have no format structure, just up | ||
510 | to `AVATAR_DATA_MAX_CHUNK_SIZE` (56) bytes of raw avatar image data. | ||
511 | |||
512 | |||
513 | |||
514 | The following procedure assumes that a client "A" is requesting avatar data | ||
515 | from a client "B": | ||
516 | |||
517 | - "A" must initialize its control structures and mark its data transfer | ||
518 | as not yet started. Then it requests avatar data from "B" by sending a | ||
519 | packet `PACKET_ID_AVATAR_DATA_CONTROL` with 'op' set to | ||
520 | `AVATARDATACONTROL_REQ`. The field 'bytes_received' must be present, but | ||
521 | should be set to zero and is ignored in this step. | ||
522 | |||
523 | - If "B" accepts this transfer, it answers by sending an | ||
524 | `PACKET_ID_AVATAR_DATA_START` with the fields 'format', 'hash' and | ||
525 | 'data_length' set to the respective values from the current avatar. | ||
526 | If "B" have no avatar set, 'format' must be `AVATARFORMAT_NONE`, 'hash' | ||
527 | must be zeroed and 'data_length' must be zero. | ||
528 | |||
529 | If "B" does not accept sending the avatar, it may send a packet | ||
530 | `PACKET_ID_AVATAR_DATA_CONTROL` with the field 'op' set to | ||
531 | `AVATARDATACONTROL_ERROR` or simply ignore this request. "A" must cope | ||
532 | with this. | ||
533 | |||
534 | - Upon receiving a `PACKET_ID_AVATAR_DATA_START`, "A" checks if it | ||
535 | has sent a data request to "B". If not, just ignores the packet. | ||
536 | |||
537 | If "A" really sent a request to "B", checks if the request was already | ||
538 | started. If true, it is an error and it just ignores the request. | ||
539 | |||
540 | Otherwise, "A" decodes the message data and checks if the avatar data | ||
541 | length stated in the field 'data_length' is acceptable (ie. less or | ||
542 | equal than `TOX_MAX_AVATAR_DATA_LENGTH`). If not, it replies with an | ||
543 | `PACKET_ID_AVATAR_DATA_CONTROL` with the field 'op' set to | ||
544 | `AVATARDATACONTROL_ERROR` (or just ignores this, "A" holds no state). | ||
545 | |||
546 | If the size is acceptable, "A" marks the request as stated, stores the | ||
547 | format, hash, and data length in the local state for user "B", sets a | ||
548 | counter for the number of bytes received from the peer and replies with | ||
549 | a `PACKET_ID_AVATAR_DATA_CONTROL` with the field 'op' set to | ||
550 | `AVATARDATACONTROL_MORE` and 'bytes_received' set to zero (as no data | ||
551 | was received yet). | ||
552 | |||
553 | - Upon receiving a `PACKET_ID_AVATAR_DATA_CONTROL` with op | ||
554 | `AVATARDATACONTROL_MORE`, "B" sends an `PACKET_ID_AVATAR_DATA_PUSH` | ||
555 | with up to `AVATAR_DATA_MAX_CHUNK_SIZE` bytes of data from the avatar, | ||
556 | starting from the offset stated in the field 'bytes_received'. | ||
557 | |||
558 | If the requested offset is invalid, "B" replies with a | ||
559 | `PACKET_ID_AVATAR_DATA_CONTROL` with the field 'op' set to | ||
560 | `AVATARDATACONTROL_ERROR`. | ||
561 | |||
562 | "B" must have full control of the amount of data it sends to "A" and | ||
563 | may, at any time, abort the transfer by sending a | ||
564 | `PACKET_ID_AVATAR_DATA_CONTROL` with the field 'op' set to | ||
565 | `AVATARDATACONTROL_ERROR`. This may happens, for example, if some limit | ||
566 | was hit or a network data usage throttle enabled. A rationale for this | ||
567 | procedures is available in section "Security considerations". | ||
568 | |||
569 | - Upon receiving a `PACKET_ID_AVATAR_DATA_PUSH`, "A" checks if the total | ||
570 | length of the data already stored in the receiving buffer plus the data | ||
571 | present in the push packet is still less or equal than | ||
572 | `TOX_MAX_AVATAR_DATA_LENGTH`. If invalid, it replies with a | ||
573 | `PACKET_ID_AVATAR_DATA_CONTROL` with the field 'op' set to | ||
574 | `AVATARDATACONTROL_ERROR`. | ||
575 | |||
576 | If valid, "A" updates the 'bytes_received' counter and concatenates the | ||
577 | newly arrived data to the buffer. | ||
578 | |||
579 | The "A" checks if all the data was already received by comparing the | ||
580 | counter 'bytes_received' with the field 'total_length'. If they are | ||
581 | equal, "A" takes a SHA-256 hash of the data and compares it with the | ||
582 | hash stored in the field 'hash' received from the first | ||
583 | `PACKET_ID_AVATAR_DATA_START`. | ||
584 | |||
585 | If the hashes match, the avatar data was correctly received and "A" | ||
586 | triggers the avatar data callback, and clears all the temporary data, | ||
587 | finishing the process. | ||
588 | |||
589 | If not all data was received, "A" requests more data by sending a | ||
590 | `PACKET_ID_AVATAR_DATA_CONTROL` with the field 'op' set to | ||
591 | `AVATARDATACONTROL_MORE` and 'bytes_received' set to the new offset. | ||
592 | |||
593 | Client "A" is always responsible for controlling the transfer and | ||
594 | validating the data received. "B" don't need to keep any state for the | ||
595 | protocol, have full control over the data sent and should implement | ||
596 | some transfer limit for the data it sends. | ||
597 | |||
598 | This "chatty" protocol mitigates a potential amplification attack, | ||
599 | i.e., a malicious friend sending a very small data packet that causes | ||
600 | another user to send a larger amount of data. The hash validation | ||
601 | ensures that the avatar data is correct even if "B" changed its avatar | ||
602 | data in the middle of the transfer. A rationale for this procedures is | ||
603 | available in section "Security considerations". | ||
604 | |||
605 | - Any peer receiving a `PACKET_ID_AVATAR_DATA_CONTROL` with the field 'op' | ||
606 | set to `AVATARDATACONTROL_ERROR` clears any existing control state and | ||
607 | finishes sending or receiving data. | ||
608 | |||
609 | |||
610 | |||
611 | |||
612 | |||
613 | ## Security considerations | ||
614 | |||
615 | The major security implication of background data transfers of large objects, | ||
616 | like avatars, is the possibility of exhausting the network resources from a | ||
617 | client. This problem is exacerbated when there is the possibility of an | ||
618 | amplification attack as happens, for example, when sending a very small | ||
619 | avatar request message will force the user to reply with a larger avatar | ||
620 | data message. | ||
621 | |||
622 | The present proposal mitigates this situation by: | ||
623 | |||
624 | - Only transferring data between previously authenticated friends; | ||
625 | |||
626 | - Enforcing strict limits on the avatar data size; | ||
627 | |||
628 | - Providing an alternate, smaller, message to cooperative users refresh | ||
629 | avatar information when nothing has changed (`PACKET_ID_AVATAR_INFO`); | ||
630 | |||
631 | - Making the avatar data transfer chatty: The user requesting avatar data | ||
632 | can not force a peer to send large amounts of data in a single shot and | ||
633 | must request new chunks as needed. The sender will never send more that | ||
634 | 1 kB of data in a single push and have ultimate control over the amount | ||
635 | of data sent in a chunk; | ||
636 | |||
637 | - Having per-friend data transfer limit. As the current protocol still | ||
638 | allows an user to request an infinite data stream by asking the the | ||
639 | same offset of the avatar again and again, the implementation limits | ||
640 | the amount of data a single user can request for some time. For now, | ||
641 | the library will not allow an user to request more than | ||
642 | `10*TOX_MAX_AVATAR_DATA_LENGTH` in less than 20 minutes; | ||
643 | |||
644 | - Making the requester responsible for storing partial data and state | ||
645 | information; | ||
646 | |||
647 | - (currently not implemented) Treating avatar data transfers as a low | ||
648 | priority operation, handled only if no other packets are being | ||
649 | processed. | ||
650 | |||
651 | Another problem present in the avatars is the possibility of a friend send | ||
652 | a maliciously crafted image intended to exploit vulnerabilities in image | ||
653 | decoders. Without an intermediate server to recompress and validate and | ||
654 | convert the images to neutral formats, the client applications must handle | ||
655 | this situation by themselves using stable and secure image libraries and | ||
656 | imposing limits on the maximum amount of system resources the decoding | ||
657 | process can take. Images coming from Tox friends must be treated in the same | ||
658 | way as images coming from random Internet sources. | ||