libgnunetchat

library for GNUnet Messenger
Log | Files | Refs | README | LICENSE

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 }