gnunet_chat_util.c (11570B)
1 /* 2 This file is part of GNUnet. 3 Copyright (C) 2021--2024 GNUnet e.V. 4 5 GNUnet is free software: you can redistribute it and/or modify it 6 under the terms of the GNU Affero General Public License as published 7 by the Free Software Foundation, either version 3 of the License, 8 or (at your option) any later version. 9 10 GNUnet is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Affero General Public License for more details. 14 15 You should have received a copy of the GNU Affero General Public License 16 along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 SPDX-License-Identifier: AGPL3.0-or-later 19 */ 20 /* 21 * @author Tobias Frisch 22 * @file gnunet_chat_util.c 23 */ 24 25 #include "gnunet_chat_util.h" 26 27 #include <gnunet/gnunet_common.h> 28 #include <gnunet/gnunet_messenger_service.h> 29 30 static const char label_prefix_of_contact [] = "contact"; 31 static const char label_prefix_of_group [] = "group"; 32 33 static const char identity_prefix_of_lobby [] = "_gnunet_chat_lobby"; 34 35 void 36 util_shorthash_from_member (const struct GNUNET_MESSENGER_Contact *member, 37 struct GNUNET_ShortHashCode *shorthash) 38 { 39 GNUNET_assert(shorthash); 40 41 const size_t id = GNUNET_MESSENGER_contact_get_id(member); 42 43 memset(shorthash, 0, sizeof(*shorthash)); 44 GNUNET_memcpy( 45 shorthash, 46 &id, 47 sizeof(id) < sizeof(*shorthash) ? sizeof(id) : sizeof(*shorthash) 48 ); 49 } 50 51 void 52 util_shorthash_from_discourse_id (const struct GNUNET_CHAT_DiscourseId *id, 53 struct GNUNET_ShortHashCode *shorthash) 54 { 55 GNUNET_assert(shorthash); 56 57 memset(shorthash, 0, sizeof(*shorthash)); 58 GNUNET_memcpy( 59 shorthash, 60 id, 61 sizeof(*id) < sizeof(*shorthash) ? sizeof(*id) : sizeof(*shorthash) 62 ); 63 } 64 65 void 66 util_discourse_id_from_shorthash (const struct GNUNET_ShortHashCode *shorthash, 67 struct GNUNET_CHAT_DiscourseId *id) 68 { 69 GNUNET_assert(id); 70 71 memset(id, 0, sizeof(*id)); 72 GNUNET_memcpy( 73 id, 74 shorthash, 75 sizeof(*id) < sizeof(*shorthash) ? sizeof(*id) : sizeof(*shorthash) 76 ); 77 } 78 79 void 80 util_set_name_field (const char *name, 81 char **field) 82 { 83 GNUNET_assert(field); 84 85 if (*field) 86 GNUNET_free(*field); 87 88 if (name) 89 *field = GNUNET_strdup(name); 90 else 91 *field = NULL; 92 } 93 94 enum GNUNET_GenericReturnValue 95 util_hash_file (const char *filename, 96 struct GNUNET_HashCode *hash) 97 { 98 GNUNET_assert((filename) && (hash)); 99 100 uint64_t size; 101 102 if (GNUNET_OK != GNUNET_DISK_file_size(filename, &size, GNUNET_NO, GNUNET_YES)) 103 return GNUNET_SYSERR; 104 105 struct GNUNET_DISK_FileHandle *file = GNUNET_DISK_file_open( 106 filename, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_USER_READ 107 ); 108 109 if (!file) 110 return GNUNET_SYSERR; 111 112 struct GNUNET_DISK_MapHandle *mapping; 113 const void* data; 114 115 if (size > 0) 116 { 117 data = GNUNET_DISK_file_map( 118 file, &mapping, GNUNET_DISK_MAP_TYPE_READ, size 119 ); 120 121 if ((!data) || (!mapping)) 122 { 123 GNUNET_DISK_file_close(file); 124 return GNUNET_SYSERR; 125 } 126 } 127 else 128 { 129 mapping = NULL; 130 data = NULL; 131 } 132 133 GNUNET_CRYPTO_hash(data, size, hash); 134 135 if (mapping) 136 GNUNET_DISK_file_unmap(mapping); 137 138 GNUNET_DISK_file_close(file); 139 return GNUNET_OK; 140 } 141 142 enum GNUNET_GenericReturnValue 143 util_encrypt_file (const char *filename, 144 const struct GNUNET_HashCode *hash, 145 const struct GNUNET_CRYPTO_SymmetricSessionKey *key) 146 { 147 GNUNET_assert((filename) && (hash)); 148 149 uint64_t size; 150 151 if (GNUNET_OK != GNUNET_DISK_file_size(filename, &size, GNUNET_NO, GNUNET_YES)) 152 return GNUNET_SYSERR; 153 154 struct GNUNET_DISK_FileHandle *file = GNUNET_DISK_file_open( 155 filename, GNUNET_DISK_OPEN_READWRITE, 156 GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE 157 ); 158 159 if (!file) 160 return GNUNET_SYSERR; 161 162 if (!size) 163 return GNUNET_DISK_file_close(file); 164 165 struct GNUNET_DISK_MapHandle *mapping; 166 const void* data = GNUNET_DISK_file_map( 167 file, &mapping, GNUNET_DISK_MAP_TYPE_READWRITE, size 168 ); 169 170 if ((!data) || (!mapping)) 171 { 172 GNUNET_DISK_file_close(file); 173 return GNUNET_SYSERR; 174 } 175 176 struct GNUNET_CRYPTO_SymmetricInitializationVector iv; 177 const uint64_t block_size = 1024*1024; 178 ssize_t result = 0; 179 180 const uint64_t blocks = ((size + block_size - 1) / block_size); 181 182 if (!key) 183 goto skip_encryption; 184 185 for (uint64_t i = 0; i < blocks; i++) 186 { 187 const uint64_t index = (blocks - i - 1); 188 const uint64_t offset = block_size * index; 189 190 const uint64_t remaining = (size - offset); 191 void* location = ((uint8_t*) data) + offset; 192 193 if (index > 0) 194 memcpy(&iv, ((uint8_t*) data) + (block_size * (index - 1)), sizeof(iv)); 195 else 196 GNUNET_CRYPTO_symmetric_derive_iv(&iv, key, hash, sizeof(hash), NULL); 197 198 result = GNUNET_CRYPTO_symmetric_encrypt( 199 location, 200 remaining >= block_size? block_size : remaining, 201 key, 202 &iv, 203 location 204 ); 205 206 if (result < 0) 207 break; 208 } 209 210 skip_encryption: 211 if (GNUNET_OK != GNUNET_DISK_file_unmap(mapping)) 212 result = -1; 213 214 if (GNUNET_OK != GNUNET_DISK_file_sync(file)) 215 result = -1; 216 217 if (GNUNET_OK != GNUNET_DISK_file_close(file)) 218 result = -1; 219 220 if (result < 0) 221 return GNUNET_SYSERR; 222 223 return GNUNET_OK; 224 } 225 226 enum GNUNET_GenericReturnValue 227 util_decrypt_file (const char *filename, 228 const struct GNUNET_HashCode *hash, 229 const struct GNUNET_CRYPTO_SymmetricSessionKey *key) 230 { 231 GNUNET_assert((filename) && (hash)); 232 233 uint64_t size; 234 235 if (GNUNET_OK != GNUNET_DISK_file_size(filename, &size, GNUNET_NO, GNUNET_YES)) 236 return GNUNET_SYSERR; 237 238 struct GNUNET_DISK_FileHandle *file = GNUNET_DISK_file_open( 239 filename, GNUNET_DISK_OPEN_READWRITE, 240 GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE 241 ); 242 243 if (!file) 244 return GNUNET_SYSERR; 245 246 struct GNUNET_DISK_MapHandle *mapping = NULL; 247 void* data = GNUNET_DISK_file_map( 248 file, &mapping, GNUNET_DISK_MAP_TYPE_READWRITE, size 249 ); 250 251 if ((!data) || (!mapping)) 252 { 253 GNUNET_DISK_file_close(file); 254 return GNUNET_SYSERR; 255 } 256 257 struct GNUNET_CRYPTO_SymmetricInitializationVector iv; 258 const uint64_t block_size = 1024*1024; 259 struct GNUNET_HashCode check; 260 ssize_t result = 0; 261 262 const uint64_t blocks = ((size + block_size - 1) / block_size); 263 264 if (!key) 265 goto skip_decryption; 266 267 for (uint64_t index = 0; index < blocks; index++) 268 { 269 const uint64_t offset = block_size * index; 270 271 const uint64_t remaining = (size - offset); 272 void* location = ((uint8_t*) data) + offset; 273 274 if (index > 0) 275 memcpy(&iv, ((uint8_t*) data) + (block_size * (index - 1)), sizeof(iv)); 276 else 277 GNUNET_CRYPTO_symmetric_derive_iv(&iv, key, hash, sizeof(hash), NULL); 278 279 result = GNUNET_CRYPTO_symmetric_decrypt( 280 location, 281 remaining >= block_size? block_size : remaining, 282 key, 283 &iv, 284 location 285 ); 286 287 if (result < 0) 288 break; 289 } 290 291 skip_decryption: 292 GNUNET_CRYPTO_hash(data, size, &check); 293 294 if (0 != GNUNET_CRYPTO_hash_cmp(hash, &check)) 295 result = -1; 296 297 if (GNUNET_OK != GNUNET_DISK_file_unmap(mapping)) 298 result = -1; 299 300 if (GNUNET_OK != GNUNET_DISK_file_sync(file)) 301 result = -1; 302 303 if (GNUNET_OK != GNUNET_DISK_file_close(file)) 304 result = -1; 305 306 if (result < 0) 307 return GNUNET_SYSERR; 308 309 return GNUNET_OK; 310 } 311 312 int 313 util_get_dirname (const char *directory, 314 const char *subdir, 315 char **filename) 316 { 317 GNUNET_assert( 318 (filename) && 319 (directory) && 320 (subdir) 321 ); 322 323 return GNUNET_asprintf ( 324 filename, 325 "%s/%s", 326 directory, 327 subdir 328 ); 329 } 330 331 int 332 util_get_filename (const char *directory, 333 const char *subdir, 334 const struct GNUNET_HashCode *hash, 335 char **filename) 336 { 337 GNUNET_assert( 338 (filename) && 339 (directory) && 340 (subdir) && 341 (hash) 342 ); 343 344 char* dirname; 345 util_get_dirname(directory, subdir, &dirname); 346 347 int result = GNUNET_asprintf ( 348 filename, 349 "%s/%s", 350 dirname, 351 GNUNET_h2s_full(hash) 352 ); 353 354 GNUNET_free(dirname); 355 return result; 356 } 357 358 char* 359 util_get_lower(const char *name) 360 { 361 GNUNET_assert(name); 362 363 char *lower = GNUNET_malloc(strlen(name) + 1); 364 if (GNUNET_OK == GNUNET_STRINGS_utf8_tolower(name, lower)) 365 return lower; 366 367 GNUNET_free(lower); 368 return GNUNET_strdup(name); 369 } 370 371 int 372 util_get_context_label (enum GNUNET_CHAT_ContextType type, 373 const struct GNUNET_HashCode *hash, 374 char **label) 375 { 376 GNUNET_assert((hash) && (label)); 377 378 const char *type_string = "chat"; 379 380 switch (type) 381 { 382 case GNUNET_CHAT_CONTEXT_TYPE_CONTACT: 383 type_string = "contact"; 384 break; 385 case GNUNET_CHAT_CONTEXT_TYPE_GROUP: 386 type_string = "group"; 387 break; 388 default: 389 break; 390 } 391 392 char *low = util_get_lower(GNUNET_h2s(hash)); 393 394 int result = GNUNET_asprintf ( 395 label, 396 "%s_%s", 397 type_string, 398 low 399 ); 400 401 GNUNET_free(low); 402 return result; 403 } 404 405 enum GNUNET_CHAT_ContextType 406 util_get_context_label_type (const char *label, 407 const struct GNUNET_HashCode *hash) 408 { 409 GNUNET_assert((hash) && (label)); 410 411 enum GNUNET_CHAT_ContextType type = GNUNET_CHAT_CONTEXT_TYPE_UNKNOWN; 412 413 char *low = util_get_lower(GNUNET_h2s(hash)); 414 415 const char *sub = strstr(label, low); 416 if ((!sub) || (sub == label) || (sub[-1] != '_')) 417 goto cleanup; 418 419 const size_t len = (size_t) (sub - label - 1); 420 421 if (0 == strncmp(label, label_prefix_of_group, len)) 422 type = GNUNET_CHAT_CONTEXT_TYPE_GROUP; 423 else if (0 == strncmp(label, label_prefix_of_contact, len)) 424 type = GNUNET_CHAT_CONTEXT_TYPE_CONTACT; 425 426 cleanup: 427 GNUNET_free(low); 428 return type; 429 } 430 431 int 432 util_lobby_name (const struct GNUNET_HashCode *hash, 433 char **name) 434 { 435 GNUNET_assert((hash) && (name)); 436 437 char *low = util_get_lower(GNUNET_h2s(hash)); 438 439 int result = GNUNET_asprintf ( 440 name, 441 "%s_%s", 442 identity_prefix_of_lobby, 443 low 444 ); 445 446 GNUNET_free(low); 447 return result; 448 } 449 450 enum GNUNET_GenericReturnValue 451 util_is_lobby_name(const char *name) 452 { 453 GNUNET_assert(name); 454 455 const char *sub = strstr(name, identity_prefix_of_lobby); 456 if ((!sub) || (sub != name)) 457 return GNUNET_NO; 458 459 const size_t len = strlen(identity_prefix_of_lobby); 460 461 if (name[len] != '_') 462 return GNUNET_NO; 463 else 464 return GNUNET_YES; 465 } 466 467 enum GNUNET_CHAT_MessageKind 468 util_message_kind_from_kind (enum GNUNET_MESSENGER_MessageKind kind) 469 { 470 switch (kind) 471 { 472 case GNUNET_MESSENGER_KIND_JOIN: 473 return GNUNET_CHAT_KIND_JOIN; 474 case GNUNET_MESSENGER_KIND_LEAVE: 475 return GNUNET_CHAT_KIND_LEAVE; 476 case GNUNET_MESSENGER_KIND_NAME: 477 case GNUNET_MESSENGER_KIND_KEY: 478 case GNUNET_MESSENGER_KIND_ID: 479 return GNUNET_CHAT_KIND_CONTACT; 480 case GNUNET_MESSENGER_KIND_INVITE: 481 return GNUNET_CHAT_KIND_INVITATION; 482 case GNUNET_MESSENGER_KIND_TEXT: 483 return GNUNET_CHAT_KIND_TEXT; 484 case GNUNET_MESSENGER_KIND_FILE: 485 return GNUNET_CHAT_KIND_FILE; 486 case GNUNET_MESSENGER_KIND_DELETION: 487 return GNUNET_CHAT_KIND_DELETION; 488 case GNUNET_MESSENGER_KIND_TICKET: 489 return GNUNET_CHAT_KIND_SHARED_ATTRIBUTES; 490 case GNUNET_MESSENGER_KIND_TAG: 491 return GNUNET_CHAT_KIND_TAG; 492 case GNUNET_MESSENGER_KIND_SUBSCRIBTION: 493 return GNUNET_CHAT_KIND_DISCOURSE; 494 case GNUNET_MESSENGER_KIND_TALK: 495 return GNUNET_CHAT_KIND_DATA; 496 default: 497 return GNUNET_CHAT_KIND_UNKNOWN; 498 } 499 }