libgnunetchat

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

commit 0b05127d7b9567b80edc9f967d8faddd2a3d147e
parent 9e10880a657b23fee26e26018e4e75fa73804f9e
Author: Jacki <jacki@thejackimonster.de>
Date:   Wed, 17 Jun 2026 20:03:43 +0200

Remove additional encryption in file messages, add function to verify hashes after download

Signed-off-by: Jacki <jacki@thejackimonster.de>

Diffstat:
Minclude/gnunet/gnunet_chat_lib.h | 81+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Msrc/gnunet_chat_file.c | 68+++++++++++++++++++++-----------------------------------------------
Msrc/gnunet_chat_file.h | 14++++++--------
Msrc/gnunet_chat_lib.c | 167++++++++++++++++++++++++++++++++++---------------------------------------------
Msrc/gnunet_chat_util.c | 194-------------------------------------------------------------------------------
Msrc/gnunet_chat_util.h | 37+++----------------------------------
6 files changed, 147 insertions(+), 414 deletions(-)

diff --git a/include/gnunet/gnunet_chat_lib.h b/include/gnunet/gnunet_chat_lib.h @@ -112,7 +112,7 @@ enum GNUNET_CHAT_MessageKind GNUNET_CHAT_KIND_LOGIN = 3, /**< GNUNET_CHAT_KIND_LOGIN */ /** - * The kind to inform that the application needs to cleanup + * The kind to inform that the application needs to cleanup * resources related to the currently connected account. */ GNUNET_CHAT_KIND_LOGOUT = 4, /**< GNUNET_CHAT_KIND_LOGOUT */ @@ -316,7 +316,7 @@ typedef enum GNUNET_GenericReturnValue * @param[in] uri Chat URI of the lobby or NULL on error */ typedef void -(*GNUNET_CHAT_LobbyCallback) (void *cls, +(*GNUNET_CHAT_LobbyCallback) (void *cls, const struct GNUNET_CHAT_Uri *uri); /** @@ -677,7 +677,7 @@ const char* GNUNET_CHAT_get_key (const struct GNUNET_CHAT_Handle *handle); /** - * Updates an attribute of a chat handle for related communication under a given + * Updates an attribute of a chat handle for related communication under a given * <i>name</i> and a custom <i>value</i>. * * @param[in,out] handle Chat handle @@ -690,7 +690,7 @@ GNUNET_CHAT_set_attribute (struct GNUNET_CHAT_Handle *handle, const char *value); /** - * Deletes an attribute of a chat <i>handle</i> for related communication + * Deletes an attribute of a chat <i>handle</i> for related communication * under a given <i>name</i>. * * @param[in,out] handle Chat handle @@ -701,7 +701,7 @@ GNUNET_CHAT_delete_attribute (struct GNUNET_CHAT_Handle *handle, const char *name); /** - * Calls an optional <i>callback</i> for each attribute of a given chat + * Calls an optional <i>callback</i> for each attribute of a given chat * <i>handle</i>. * * @param[in,out] handle Chat handle @@ -740,7 +740,7 @@ GNUNET_CHAT_unshare_attribute_from (struct GNUNET_CHAT_Handle *handle, const char *name); /** - * Calls an optional <i>callback</i> for each attribute of a given chat + * Calls an optional <i>callback</i> for each attribute of a given chat * <i>handle</i> shared with a specific chat <i>contact</i>. * * @param[in,out] handle Chat handle @@ -826,7 +826,7 @@ GNUNET_CHAT_lobby_join (struct GNUNET_CHAT_Handle *handle, const struct GNUNET_CHAT_Uri *uri); /** - * Requests a file with a given chat <i>handle</i> from a selected chat + * Requests a file with a given chat <i>handle</i> from a selected chat * <i>uri</i> to potentially download it. * * @param[in,out] handle Chat handle @@ -903,7 +903,7 @@ GNUNET_CHAT_iterate_contacts (struct GNUNET_CHAT_Handle *handle, void *cls); /** - * Returns the chat contact matching a given chat <i>handle</i>'s current + * Returns the chat contact matching a given chat <i>handle</i>'s current * account. * * @param[in,out] handle Chat handle @@ -922,7 +922,7 @@ const char* GNUNET_CHAT_account_get_name (const struct GNUNET_CHAT_Account *account); /** - * Calls an optional <i>callback</i> for each attribute of a given chat + * Calls an optional <i>callback</i> for each attribute of a given chat * <i>account</i> using a chat <i>handle</i>. * * @param[in,out] handle Chat handle @@ -1084,7 +1084,7 @@ GNUNET_CHAT_contact_set_blocked (struct GNUNET_CHAT_Contact *contact, * whether messages of it should be filtered. * * @param[in] contact Contact - * @return #GNUNET_YES if the contact is blocked, #GNUNET_SYSERR on failure and + * @return #GNUNET_YES if the contact is blocked, #GNUNET_SYSERR on failure and * #GNUNET_NO otherwise */ enum GNUNET_GenericReturnValue @@ -1118,7 +1118,7 @@ GNUNET_CHAT_contact_untag (struct GNUNET_CHAT_Contact *contact, * * @param[in] contact Contact * @param[in] tag Tag - * @return #GNUNET_YES if the contact is tagged, #GNUNET_SYSERR on failure and + * @return #GNUNET_YES if the contact is tagged, #GNUNET_SYSERR on failure and * #GNUNET_NO otherwise */ enum GNUNET_GenericReturnValue @@ -1126,7 +1126,7 @@ GNUNET_CHAT_contact_is_tagged (const struct GNUNET_CHAT_Contact *contact, const char *tag); /** - * Calls an optional <i>callback</i> for each tag of a given chat + * Calls an optional <i>callback</i> for each tag of a given chat * <i>contact</i>. * * @param[in] contact Chat contact @@ -1140,7 +1140,7 @@ GNUNET_CHAT_contact_iterate_tags (struct GNUNET_CHAT_Contact *contact, void *cls); /** - * Calls an optional <i>callback</i> for each attribute of a given chat + * Calls an optional <i>callback</i> for each attribute of a given chat * <i>contact</i>. * * @param[in,out] contact Chat contact @@ -1349,8 +1349,8 @@ GNUNET_CHAT_context_send_read_receipt (struct GNUNET_CHAT_Context *context, struct GNUNET_CHAT_Message *message); /** - * Uploads a local file specified via its <i>path</i> using symmetric encryption - * and shares the regarding information to download and decrypt it in a given + * Uploads a local file specified via its <i>path</i> and shares the + * regarding information to download and verify it in a given * chat <i>context</i>. * * @param[in,out] context Chat context @@ -1392,7 +1392,7 @@ GNUNET_CHAT_context_send_tag (struct GNUNET_CHAT_Context *context, const char *tag); /** - * Opens a chat discourse under a specific <i>id</i> in a given chat + * Opens a chat discourse under a specific <i>id</i> in a given chat * <i>context</i> to send data live to other contacts. * * @param[in,out] context Chat context @@ -1432,7 +1432,7 @@ GNUNET_CHAT_context_iterate_files (struct GNUNET_CHAT_Context *context, void *cls); /** - * Iterates through the discourses of a given chat <i>context</i> with a + * Iterates through the discourses of a given chat <i>context</i> with a * selected callback and custom closure. * * @param[in,out] context Chat context @@ -1483,7 +1483,7 @@ struct GNUNET_CHAT_Contact* GNUNET_CHAT_message_get_recipient (const struct GNUNET_CHAT_Message *message); /** - * Returns #GNUNET_YES if the <i>message</i> was sent by the related chat + * Returns #GNUNET_YES if the <i>message</i> was sent by the related chat * handle, otherwise it returns #GNUNET_NO. * * @param[in] message Message @@ -1504,35 +1504,35 @@ enum GNUNET_GenericReturnValue GNUNET_CHAT_message_is_private (const struct GNUNET_CHAT_Message *message); /** - * Returns #GNUNET_YES if the <i>message</i> was received recently by related + * Returns #GNUNET_YES if the <i>message</i> was received recently by related * chat handle, otherwise it returns #GNUNET_NO. * * @param[in] message Message * @return #GNUNET_YES if the message was received recently, - * otherwise #GNUNET_NO + * otherwise #GNUNET_NO */ enum GNUNET_GenericReturnValue GNUNET_CHAT_message_is_recent (const struct GNUNET_CHAT_Message *message); /** - * Returns #GNUNET_YES if the <i>message</i> was received because of an + * Returns #GNUNET_YES if the <i>message</i> was received because of an * update by related chat handle, otherwise it returns #GNUNET_NO. * * @param[in] message Message * @return #GNUNET_YES if the message was received to update - * a previous message, otherwise #GNUNET_NO + * a previous message, otherwise #GNUNET_NO */ enum GNUNET_GenericReturnValue GNUNET_CHAT_message_is_update (const struct GNUNET_CHAT_Message *message); /** - * Returns #GNUNET_YES if the <i>message</i> was received because of a - * deletion by related chat handle or if it has been deleted internally, + * Returns #GNUNET_YES if the <i>message</i> was received because of a + * deletion by related chat handle or if it has been deleted internally, * otherwise it returns #GNUNET_NO. * * @param[in] message Message * @return #GNUNET_YES if the message was received to delete - * a previous message, otherwise #GNUNET_NO + * a previous message, otherwise #GNUNET_NO */ enum GNUNET_GenericReturnValue GNUNET_CHAT_message_is_deleted (const struct GNUNET_CHAT_Message *message); @@ -1596,7 +1596,7 @@ void* GNUNET_CHAT_message_get_user_pointer (const struct GNUNET_CHAT_Message *message); /** - * Returns the account of a given <i>message</i> which is either + * Returns the account of a given <i>message</i> which is either * its sender or target of the message depending on the kind of * the message, otherwise it returns NULL. * @@ -1661,7 +1661,7 @@ GNUNET_CHAT_message_delete (struct GNUNET_CHAT_Message *message, unsigned int delay); /** - * Iterates through the tag messages in the context of a given + * Iterates through the tag messages in the context of a given * <i>message</i>. * * @param[in,out] message Message @@ -1675,7 +1675,7 @@ GNUNET_CHAT_message_iterate_tags (struct GNUNET_CHAT_Message *message, void *cls); /** - * Returns the amount of data from a given chat <i>message</i> if its + * Returns the amount of data from a given chat <i>message</i> if its * kind is #GNUNET_CHAT_KIND_DATA, otherwise it returns zero. * * @param[in] message Message @@ -1692,7 +1692,7 @@ GNUNET_CHAT_message_available (const struct GNUNET_CHAT_Message *message); * @param[in] message Message * @param[out] data Data buffer * @param[in] size Data size - * @return #GNUNET_OK on success, #GNUNET_NO if there's missing data + * @return #GNUNET_OK on success, #GNUNET_NO if there's missing data * to read, otherwise #GNUNET_SYSERR on failure */ enum GNUNET_GenericReturnValue @@ -1702,12 +1702,12 @@ GNUNET_CHAT_message_read (const struct GNUNET_CHAT_Message *message, /** * Feeds the data from a given chat <i>message</i> if its kind is - * #GNUNET_CHAT_KIND_DATA into a specific file descriptor (of pipe for + * #GNUNET_CHAT_KIND_DATA into a specific file descriptor (of pipe for * example). * * @param[in] message Message * @param[in] fd File descriptor - * @return #GNUNET_OK on success, #GNUNET_NO if there's not enough data + * @return #GNUNET_OK on success, #GNUNET_NO if there's not enough data * to read, otherwise #GNUNET_SYSERR on failure */ enum GNUNET_GenericReturnValue @@ -1754,7 +1754,7 @@ uint64_t GNUNET_CHAT_file_get_local_size (const struct GNUNET_CHAT_File *file); /** - * Returns a new allocated uri to access the content of a given + * Returns a new allocated uri to access the content of a given * <i>file</i> handle. * * @param[in] file File handle @@ -1783,6 +1783,17 @@ enum GNUNET_GenericReturnValue GNUNET_CHAT_file_is_ready (const struct GNUNET_CHAT_File *file); /** + * Returns if a given <i>file</i> handle is verified + * to match its provided hash. + * + * @param[in] file File handle + * @return #GNUNET_YES when valid, #GNUNET_NO on mismatch + * and #GNUNET_SYSERR on error + */ +enum GNUNET_GenericReturnValue +GNUNET_CHAT_file_is_valid (const struct GNUNET_CHAT_File *file); + +/** * Returns the temporary file name of the decrypted file preview * of a given <i>file</i> handle. * @@ -1936,7 +1947,7 @@ enum GNUNET_GenericReturnValue GNUNET_CHAT_invitation_is_rejected (const struct GNUNET_CHAT_Invitation *invitation); /** - * Returns whether the intend from a given + * Returns whether the intend from a given * <i>invitation</i>. is to open a direct chat or * a group chat. * @@ -1959,7 +1970,7 @@ GNUNET_CHAT_discourse_get_id (const struct GNUNET_CHAT_Discourse *discourse); * Returns whether a chat <i>discourse</i> is currently open. * * @param[in] discourse Chat discourse - * @return #GNUNET_YES if the discourse is open, #GNUNET_SYSERR on failure and + * @return #GNUNET_YES if the discourse is open, #GNUNET_SYSERR on failure and * #GNUNET_NO otherwise. */ enum GNUNET_GenericReturnValue @@ -2020,7 +2031,7 @@ int GNUNET_CHAT_discourse_get_fd (const struct GNUNET_CHAT_Discourse *discourse); /** - * Iterates through the subscribed chat contacts of a given chat <i>discourse</i> + * Iterates through the subscribed chat contacts of a given chat <i>discourse</i> * with a selected callback and custom closure. * * @param[in,out] discourse Chat discourse diff --git a/src/gnunet_chat_file.c b/src/gnunet_chat_file.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2021--2024 GNUnet e.V. + Copyright (C) 2021--2024, 2026 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published @@ -26,9 +26,11 @@ #include "gnunet_chat_context.h" #include "gnunet_chat_handle.h" +#include "gnunet_chat_util.h" #include <gnunet/gnunet_common.h> #include <gnunet/gnunet_fs_service.h> +#include <gnunet/gnunet_util_lib.h> #include <string.h> static void @@ -61,28 +63,31 @@ file_create_from_message (struct GNUNET_CHAT_Handle *handle, { GNUNET_assert((handle) && (message) && (message->uri)); - struct GNUNET_CHAT_File* file = GNUNET_new(struct GNUNET_CHAT_File); + struct GNUNET_FS_Uri *uri = GNUNET_FS_uri_parse(message->uri, NULL); - if (!file) + if (!uri) return NULL; - file->handle = handle; - file->name = GNUNET_strndup(message->name, NAME_MAX); - - file->key = GNUNET_new(struct GNUNET_CRYPTO_SymmetricSessionKey); + const struct GNUNET_HashCode *hash = GNUNET_FS_uri_chk_get_file_hash(uri); - if (!(file->key)) + if ((!hash) || (0 != GNUNET_CRYPTO_hash_cmp(hash, &(message->hash)))) { - GNUNET_free(file); + GNUNET_FS_uri_destroy(uri); return NULL; } - GNUNET_memcpy(file->key, &(message->key), - sizeof(struct GNUNET_CRYPTO_SymmetricSessionKey)); - GNUNET_memcpy(&(file->hash), &(message->hash), sizeof(file->hash)); + struct GNUNET_CHAT_File* file = GNUNET_new(struct GNUNET_CHAT_File); + + if (!file) + return NULL; + + file->handle = handle; + file->name = GNUNET_strndup(message->name, NAME_MAX); + + memcpy(&(file->hash), hash, sizeof(file->hash)); file->meta = GNUNET_FS_meta_data_create(); - file->uri = GNUNET_FS_uri_parse(message->uri, NULL); + file->uri = uri; file_initialize(file); @@ -108,13 +113,11 @@ file_create_from_chk_uri (struct GNUNET_CHAT_Handle *handle, file->handle = handle; file->name = NULL; - file->key = NULL; - - GNUNET_memcpy(&(file->hash), hash, sizeof(file->hash)); + memcpy(&(file->hash), hash, sizeof(file->hash)); file->meta = GNUNET_FS_meta_data_create(); file->uri = GNUNET_FS_uri_dup(uri); - + file_initialize(file); return file; @@ -123,8 +126,7 @@ file_create_from_chk_uri (struct GNUNET_CHAT_Handle *handle, struct GNUNET_CHAT_File* file_create_from_disk (struct GNUNET_CHAT_Handle *handle, const char *name, - const struct GNUNET_HashCode *hash, - const struct GNUNET_CRYPTO_SymmetricSessionKey *key) + const struct GNUNET_HashCode *hash) { GNUNET_assert((handle) && (name) && (hash)); @@ -136,24 +138,6 @@ file_create_from_disk (struct GNUNET_CHAT_Handle *handle, file->handle = handle; file->name = GNUNET_strndup(name, NAME_MAX); - if (!key) - { - file->key = NULL; - goto skip_key; - } - - file->key = GNUNET_new(struct GNUNET_CRYPTO_SymmetricSessionKey); - - if (!(file->key)) - { - GNUNET_free(file); - return NULL; - } - - GNUNET_memcpy(file->key, key, - sizeof(struct GNUNET_CRYPTO_SymmetricSessionKey)); - -skip_key: GNUNET_memcpy(&(file->hash), hash, sizeof(file->hash)); file->meta = GNUNET_FS_meta_data_create(); @@ -176,9 +160,6 @@ file_destroy (struct GNUNET_CHAT_File *file) if (!(file->preview)) goto skip_preview; - if (!(file->key)) - goto skip_filename; - char *filename = handle_create_file_path( file->handle, &(file->hash) ); @@ -249,9 +230,6 @@ skip_preview: if (file->meta) GNUNET_FS_meta_data_destroy(file->meta); - if (file->key) - GNUNET_free(file->key); - if (file->name) GNUNET_free(file->name); @@ -350,10 +328,6 @@ file_update_upload (struct GNUNET_CHAT_File *file, msg.header.kind = GNUNET_MESSENGER_KIND_FILE; - if (file->key) - GNUNET_memcpy(&(msg.body.file.key), file->key, - sizeof(struct GNUNET_CRYPTO_SymmetricSessionKey)); - GNUNET_memcpy(&(msg.body.file.hash), &(file->hash), sizeof(file->hash)); GNUNET_strlcpy(msg.body.file.name, file->name, NAME_MAX); msg.body.file.uri = GNUNET_FS_uri_to_string(file->uri); diff --git a/src/gnunet_chat_file.h b/src/gnunet_chat_file.h @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2021--2024 GNUnet e.V. + Copyright (C) 2021--2024, 2026 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published @@ -25,6 +25,7 @@ #ifndef GNUNET_CHAT_FILE_H_ #define GNUNET_CHAT_FILE_H_ +#include <gnunet/gnunet_common.h> #include <gnunet/gnunet_fs_service.h> #include <gnunet/gnunet_messenger_service.h> #include <gnunet/gnunet_util_lib.h> @@ -76,7 +77,7 @@ struct GNUNET_CHAT_File char *name; struct GNUNET_HashCode hash; - struct GNUNET_CRYPTO_SymmetricSessionKey *key; + struct GNUNET_FS_MetaData *meta; struct GNUNET_FS_Uri *uri; @@ -125,21 +126,18 @@ file_create_from_chk_uri (struct GNUNET_CHAT_Handle *handle, /** * Creates a chat file handle from a local file on disk - * under a given <i>name</i> using a <i>hash</i> and a - * selected symmetric <i>key</i> with a selected chat - * <i>handle</i>. + * under a given <i>name</i> using a <i>hash</i> with + * a selected chat <i>handle</i>. * * @param[in,out] handle Chat handle * @param[in] name File name * @param[in] hash File hash - * @param[in] key Symmetric key * @return New chat file handle */ struct GNUNET_CHAT_File* file_create_from_disk (struct GNUNET_CHAT_Handle *handle, const char *name, - const struct GNUNET_HashCode *hash, - const struct GNUNET_CRYPTO_SymmetricSessionKey *key); + const struct GNUNET_HashCode *hash); /** * Destroys a chat <i>file</i> handle and frees its memory. diff --git a/src/gnunet_chat_lib.c b/src/gnunet_chat_lib.c @@ -211,14 +211,14 @@ GNUNET_CHAT_connect (struct GNUNET_CHAT_Handle *handle, handle->next_secret, sizeof(*(handle->next_secret)) ); - + GNUNET_free(handle->next_secret); } if ((secret) && (secret_len > 0)) { handle->next_secret = GNUNET_new(struct GNUNET_HashCode); - + if (handle->next_secret) GNUNET_CRYPTO_hash(secret, secret_len, handle->next_secret); } @@ -246,7 +246,7 @@ GNUNET_CHAT_disconnect (struct GNUNET_CHAT_Handle *handle) if ((!handle) || (handle->destruction)) return; - + if (handle->connection) GNUNET_SCHEDULER_cancel(handle->connection); @@ -732,10 +732,10 @@ GNUNET_CHAT_request_file (struct GNUNET_CHAT_Handle *handle, { GNUNET_CHAT_VERSION_ASSERT(); - if ((!handle) || (handle->destruction) || + if ((!handle) || (handle->destruction) || (!uri) || (GNUNET_CHAT_URI_TYPE_FS != uri->type)) return NULL; - + if (!GNUNET_FS_uri_test_chk(uri->fs.uri)) return NULL; @@ -759,13 +759,13 @@ GNUNET_CHAT_request_file (struct GNUNET_CHAT_Handle *handle, if (!file) return NULL; - if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(handle->files, hash, file, + if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(handle->files, hash, file, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) { file_destroy(file); file = NULL; } - + return file; } @@ -781,7 +781,7 @@ GNUNET_CHAT_upload_file (struct GNUNET_CHAT_Handle *handle, if ((!handle) || (handle->destruction) || (!path)) return NULL; - + struct GNUNET_HashCode hash; if (GNUNET_OK != util_hash_file(path, &hash)) return NULL; @@ -814,8 +814,7 @@ GNUNET_CHAT_upload_file (struct GNUNET_CHAT_Handle *handle, file = file_create_from_disk( handle, basename(p), - &hash, - NULL + &hash ); GNUNET_free(p); @@ -1268,7 +1267,7 @@ GNUNET_CHAT_contact_set_blocked (struct GNUNET_CHAT_Contact *contact, struct GNUNET_CHAT_ContactIterateContexts it; it.contact = contact; it.tag = NULL; - + if (GNUNET_NO == blocked) it.cb = contact_untag; else if (GNUNET_YES == blocked) @@ -1384,7 +1383,7 @@ GNUNET_CHAT_contact_get_attributes (struct GNUNET_CHAT_Contact *contact, struct GNUNET_CHAT_InternalTickets *tickets; tickets = contact->tickets_head; - + while (tickets) { ticket_consume( @@ -1663,7 +1662,7 @@ GNUNET_CHAT_context_request (struct GNUNET_CHAT_Context *context) if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains( handle->contexts, &(key.hash))) return GNUNET_SYSERR; - + const struct GNUNET_PeerIdentity *pid = handle->pid; if (!pid) @@ -1736,7 +1735,7 @@ GNUNET_CHAT_context_get_contact (struct GNUNET_CHAT_Context *context) find.ignore_key = GNUNET_MESSENGER_get_key(context->handle->messenger); else find.ignore_key = NULL; - + find.contact = NULL; int member_count = GNUNET_MESSENGER_iterate_members( @@ -1901,22 +1900,12 @@ GNUNET_CHAT_context_send_file (struct GNUNET_CHAT_Context *context, return NULL; } - struct GNUNET_CRYPTO_SymmetricSessionKey key; - GNUNET_CRYPTO_symmetric_create_session_key(&key); - - if (GNUNET_OK != util_encrypt_file(filename, &hash, &key)) - { - GNUNET_free(filename); - return NULL; - } - char* p = GNUNET_strdup(path); file = file_create_from_disk( context->handle, basename(p), - &hash, - &key + &hash ); GNUNET_free(p); @@ -1970,7 +1959,7 @@ GNUNET_CHAT_context_share_file (struct GNUNET_CHAT_Context *context, { GNUNET_CHAT_VERSION_ASSERT(); - if ((!context) || (!file) || + if ((!context) || (!file) || (!(file->name)) || (strlen(file->name) > NAME_MAX) || (!(file->uri)) || (!(context->room))) return GNUNET_SYSERR; @@ -1980,12 +1969,6 @@ GNUNET_CHAT_context_share_file (struct GNUNET_CHAT_Context *context, msg.header.kind = GNUNET_MESSENGER_KIND_FILE; - if (file->key) - GNUNET_memcpy(&(msg.body.file.key), file->key, - sizeof(struct GNUNET_CRYPTO_SymmetricSessionKey)); - else - memset(&(msg.body.file.key), 0, sizeof(msg.body.file.key)); - GNUNET_memcpy(&(msg.body.file.hash), &(file->hash), sizeof(file->hash)); GNUNET_strlcpy(msg.body.file.name, file->name, NAME_MAX); msg.body.file.uri = GNUNET_FS_uri_to_string(file->uri); @@ -2314,7 +2297,7 @@ GNUNET_CHAT_message_is_tagged (const struct GNUNET_CHAT_Message *message, const struct GNUNET_CHAT_InternalTagging *tagging = GNUNET_CONTAINER_multihashmap_get( message->context->taggings, &(message->hash)); - + if (!tagging) return GNUNET_NO; @@ -2332,7 +2315,7 @@ GNUNET_CHAT_message_get_read_receipt (struct GNUNET_CHAT_Message *message, { GNUNET_CHAT_VERSION_ASSERT(); - if ((!message) || (GNUNET_YES != message_has_msg(message)) || + if ((!message) || (GNUNET_YES != message_has_msg(message)) || (!(message->context))) return GNUNET_SYSERR; @@ -2421,7 +2404,7 @@ GNUNET_CHAT_message_get_file (const struct GNUNET_CHAT_Message *message) { GNUNET_CHAT_VERSION_ASSERT(); - if ((!message) || (GNUNET_YES != message_has_msg(message)) || + if ((!message) || (GNUNET_YES != message_has_msg(message)) || (!(message->context))) return NULL; @@ -2440,7 +2423,7 @@ GNUNET_CHAT_message_get_invitation (const struct GNUNET_CHAT_Message *message) { GNUNET_CHAT_VERSION_ASSERT(); - if ((!message) || (GNUNET_YES != message_has_msg(message)) || + if ((!message) || (GNUNET_YES != message_has_msg(message)) || (!(message->context))) return NULL; @@ -2462,9 +2445,9 @@ GNUNET_CHAT_message_get_discourse (const struct GNUNET_CHAT_Message *message) if ((!message) || (GNUNET_YES != message_has_msg(message)) || (!(message->context)) || (!(message->context->discourses))) return NULL; - + struct GNUNET_CHAT_Discourse *discourse; - + if (GNUNET_MESSENGER_KIND_SUBSCRIBTION == message->msg->header.kind) discourse = GNUNET_CONTAINER_multishortmap_get( message->context->discourses, @@ -2485,7 +2468,7 @@ GNUNET_CHAT_message_get_target (const struct GNUNET_CHAT_Message *message) { GNUNET_CHAT_VERSION_ASSERT(); - if ((!message) || (GNUNET_YES != message_has_msg(message)) || + if ((!message) || (GNUNET_YES != message_has_msg(message)) || (!(message->context))) return NULL; @@ -2510,10 +2493,10 @@ GNUNET_CHAT_message_delete (struct GNUNET_CHAT_Message *message, { GNUNET_CHAT_VERSION_ASSERT(); - if ((!message) || (GNUNET_YES != message_has_msg(message)) || + if ((!message) || (GNUNET_YES != message_has_msg(message)) || (!(message->context))) return GNUNET_SYSERR; - + struct GNUNET_TIME_Relative rel = GNUNET_TIME_relative_multiply( GNUNET_TIME_relative_get_second_(), delay ); @@ -2540,7 +2523,7 @@ GNUNET_CHAT_message_iterate_tags (struct GNUNET_CHAT_Message *message, const struct GNUNET_CHAT_InternalTagging *tagging = GNUNET_CONTAINER_multihashmap_get( message->context->taggings, &(message->hash)); - + if (!tagging) return 0; @@ -2724,6 +2707,39 @@ GNUNET_CHAT_file_is_ready (const struct GNUNET_CHAT_File *file) } +enum GNUNET_GenericReturnValue +GNUNET_CHAT_file_is_valid (const struct GNUNET_CHAT_File *file) +{ + GNUNET_CHAT_VERSION_ASSERT(); + + if ((!file) || (file->status & GNUNET_CHAT_FILE_STATUS_MASK)) + return GNUNET_SYSERR; + + char *filename = handle_create_file_path( + file->handle, &(file->hash) + ); + + if (!filename) + return GNUNET_SYSERR; + + struct GNUNET_HashCode hash; + enum GNUNET_GenericReturnValue result; + + if (GNUNET_OK == util_hash_file(filename, &hash)) + { + if (0 == GNUNET_CRYPTO_hash_cmp(&hash, &(file->hash))) + result = GNUNET_YES; + else + result = GNUNET_NO; + } + else + result = GNUNET_SYSERR; + + GNUNET_free(filename); + return result; +} + + const char* GNUNET_CHAT_file_open_preview (struct GNUNET_CHAT_File *file) { @@ -2731,49 +2747,24 @@ GNUNET_CHAT_file_open_preview (struct GNUNET_CHAT_File *file) if (!file) return NULL; - + if (file->preview) return file->preview; - + char *filename = handle_create_file_path( file->handle, &(file->hash) ); if (!filename) return NULL; - + if (GNUNET_YES != GNUNET_DISK_file_test(filename)) - goto free_filename; - - if (!(file->key)) { - file->preview = filename; - return file->preview; + GNUNET_free(filename); + filename = NULL; } - - file->preview = GNUNET_DISK_mktemp( - file->name? file->name : "" - ); - - if (!(file->preview)) - goto free_filename; - - remove(file->preview); - if (GNUNET_OK != GNUNET_DISK_file_copy(filename, file->preview)) - goto cleanup_preview; - - if (GNUNET_OK == util_decrypt_file(file->preview, &(file->hash), file->key)) - goto free_filename; - - remove(file->preview); - -cleanup_preview: - GNUNET_free(file->preview); - file->preview = NULL; - -free_filename: - GNUNET_free(filename); + file->preview = filename; return file->preview; } @@ -2786,22 +2777,6 @@ GNUNET_CHAT_file_close_preview (struct GNUNET_CHAT_File *file) if ((!file) || (!(file->preview))) return; - if (!(file->key)) - goto skip_filename; - - char *filename = handle_create_file_path( - file->handle, &(file->hash) - ); - - if (!filename) - goto skip_filename; - - if (0 != strcmp(filename, file->preview)) - remove(file->preview); - - GNUNET_free(filename); - -skip_filename: GNUNET_free(file->preview); file->preview = NULL; } @@ -2872,7 +2847,7 @@ GNUNET_CHAT_file_start_download (struct GNUNET_CHAT_File *file, const uint64_t size = GNUNET_FS_uri_chk_get_file_size(file->uri); uint64_t offset; - if (GNUNET_OK != GNUNET_DISK_file_size(filename, &offset, + if (GNUNET_OK != GNUNET_DISK_file_size(filename, &offset, GNUNET_NO, GNUNET_YES)) offset = 0; @@ -3041,7 +3016,7 @@ GNUNET_CHAT_invitation_accept (struct GNUNET_CHAT_Invitation *invitation) handle->contexts, &(invitation->key.hash), context, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) goto destroy_context; - + if (GNUNET_CHAT_CONTEXT_TYPE_GROUP != context->type) { context_write_records(context); @@ -3121,10 +3096,10 @@ GNUNET_CHAT_invitation_is_rejected (const struct GNUNET_CHAT_Invitation *invitat const struct GNUNET_CHAT_InternalTagging *tagging = GNUNET_CONTAINER_multihashmap_get( invitation->context->taggings, &(invitation->hash)); - + if (!tagging) return GNUNET_NO; - + if (internal_tagging_iterate(tagging, GNUNET_NO, NULL, NULL, NULL) > 0) return GNUNET_YES; else @@ -3212,7 +3187,7 @@ GNUNET_CHAT_discourse_close (struct GNUNET_CHAT_Discourse *discourse) if ((!discourse) || (!(discourse->context)) || (!(discourse->context->room))) return; - + struct GNUNET_MESSENGER_Message msg; memset(&msg, 0, sizeof(msg)); @@ -3241,10 +3216,10 @@ GNUNET_CHAT_discourse_write (struct GNUNET_CHAT_Discourse *discourse, { GNUNET_CHAT_VERSION_ASSERT(); - if ((!discourse) || (!data) || (!(discourse->context)) || + if ((!discourse) || (!data) || (!(discourse->context)) || (!(discourse->context->room))) return GNUNET_SYSERR; - + static const uint64_t max_size = (uint16_t) ( GNUNET_MAX_MESSAGE_SIZE - GNUNET_MIN_MESSAGE_SIZE - sizeof (struct GNUNET_MESSENGER_Message) diff --git a/src/gnunet_chat_util.c b/src/gnunet_chat_util.c @@ -142,200 +142,6 @@ util_hash_file (const char *filename, return GNUNET_OK; } -enum GNUNET_GenericReturnValue -util_encrypt_file (const char *filename, - const struct GNUNET_HashCode *hash, - const struct GNUNET_CRYPTO_SymmetricSessionKey *key) -{ - GNUNET_assert((filename) && (hash)); - - uint64_t size; - - if (GNUNET_OK != GNUNET_DISK_file_size(filename, &size, GNUNET_NO, GNUNET_YES)) - return GNUNET_SYSERR; - - struct GNUNET_DISK_FileHandle *file = GNUNET_DISK_file_open( - filename, GNUNET_DISK_OPEN_READWRITE, - GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE - ); - - if (!file) - return GNUNET_SYSERR; - - if (!size) - return GNUNET_DISK_file_close(file); - - struct GNUNET_DISK_MapHandle *mapping; - const void* data = GNUNET_DISK_file_map( - file, &mapping, GNUNET_DISK_MAP_TYPE_READWRITE, size - ); - - if ((!data) || (!mapping)) - { - GNUNET_DISK_file_close(file); - return GNUNET_SYSERR; - } - - const uint64_t block_size = 1024*1024; - ssize_t result = 0; - - const uint64_t blocks = ((size + block_size - 1) / block_size); - - if (!key) - goto skip_encryption; - - struct GNUNET_CRYPTO_SymmetricInitializationVector first_iv; - - if (GNUNET_YES != GNUNET_CRYPTO_hkdf_gnunet ( - &first_iv, sizeof (first_iv), - GNUNET_CHAT_SALT_FILE, - sizeof (GNUNET_CHAT_SALT_FILE), - key, - sizeof (*key), - GNUNET_CRYPTO_kdf_arg_auto(hash))) - return GNUNET_SYSERR; - - struct GNUNET_CRYPTO_SymmetricInitializationVector iv; - - for (uint64_t i = 0; i < blocks; i++) - { - const uint64_t index = (blocks - i - 1); - const uint64_t offset = block_size * index; - - const uint64_t remaining = (size - offset); - void* location = ((uint8_t*) data) + offset; - - if (index > 0) - memcpy(&iv, ((uint8_t*) data) + (block_size * (index - 1)), sizeof(iv)); - else - memcpy(&iv, &first_iv, sizeof(iv)); - - result = GNUNET_CRYPTO_symmetric_encrypt( - location, - remaining >= block_size? block_size : remaining, - key, - &iv, - location - ); - - if (result < 0) - break; - } - -skip_encryption: - if (GNUNET_OK != GNUNET_DISK_file_unmap(mapping)) - result = -1; - - if (GNUNET_OK != GNUNET_DISK_file_sync(file)) - result = -1; - - if (GNUNET_OK != GNUNET_DISK_file_close(file)) - result = -1; - - if (result < 0) - return GNUNET_SYSERR; - - return GNUNET_OK; -} - -enum GNUNET_GenericReturnValue -util_decrypt_file (const char *filename, - const struct GNUNET_HashCode *hash, - const struct GNUNET_CRYPTO_SymmetricSessionKey *key) -{ - GNUNET_assert((filename) && (hash)); - - uint64_t size; - - if (GNUNET_OK != GNUNET_DISK_file_size(filename, &size, GNUNET_NO, GNUNET_YES)) - return GNUNET_SYSERR; - - struct GNUNET_DISK_FileHandle *file = GNUNET_DISK_file_open( - filename, GNUNET_DISK_OPEN_READWRITE, - GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE - ); - - if (!file) - return GNUNET_SYSERR; - - struct GNUNET_DISK_MapHandle *mapping = NULL; - void* data = GNUNET_DISK_file_map( - file, &mapping, GNUNET_DISK_MAP_TYPE_READWRITE, size - ); - - if ((!data) || (!mapping)) - { - GNUNET_DISK_file_close(file); - return GNUNET_SYSERR; - } - - const uint64_t block_size = 1024*1024; - struct GNUNET_HashCode check; - ssize_t result = 0; - - const uint64_t blocks = ((size + block_size - 1) / block_size); - - if (!key) - goto skip_decryption; - - struct GNUNET_CRYPTO_SymmetricInitializationVector first_iv; - - if (GNUNET_YES != GNUNET_CRYPTO_hkdf_gnunet ( - &first_iv, sizeof (first_iv), - GNUNET_CHAT_SALT_FILE, - sizeof (GNUNET_CHAT_SALT_FILE), - key, - sizeof (*key), - GNUNET_CRYPTO_kdf_arg_auto(hash))) - return GNUNET_SYSERR; - - struct GNUNET_CRYPTO_SymmetricInitializationVector iv; - - for (uint64_t index = 0; index < blocks; index++) - { - const uint64_t offset = block_size * index; - - const uint64_t remaining = (size - offset); - void* location = ((uint8_t*) data) + offset; - - if (index > 0) - memcpy(&iv, ((uint8_t*) data) + (block_size * (index - 1)), sizeof(iv)); - else - memcpy(&iv, &first_iv, sizeof(iv)); - - result = GNUNET_CRYPTO_symmetric_decrypt( - location, - remaining >= block_size? block_size : remaining, - key, - &iv, - location - ); - - if (result < 0) - break; - } - -skip_decryption: - GNUNET_CRYPTO_hash(data, size, &check); - - if (0 != GNUNET_CRYPTO_hash_cmp(hash, &check)) - result = -1; - - if (GNUNET_OK != GNUNET_DISK_file_unmap(mapping)) - result = -1; - - if (GNUNET_OK != GNUNET_DISK_file_sync(file)) - result = -1; - - if (GNUNET_OK != GNUNET_DISK_file_close(file)) - result = -1; - - if (result < 0) - return GNUNET_SYSERR; - - return GNUNET_OK; -} - int util_get_dirname (const char *directory, const char *subdir, diff --git a/src/gnunet_chat_util.h b/src/gnunet_chat_util.h @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2021--2024 GNUnet e.V. + Copyright (C) 2021--2024, 2026 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published @@ -64,7 +64,7 @@ util_shorthash_from_member (const struct GNUNET_MESSENGER_Contact *member, struct GNUNET_ShortHashCode *shorthash); /** - * Converts a discourse id into a short hash variant for map access + * Converts a discourse id into a short hash variant for map access * as key. * * @param[in] id Discourse id @@ -108,37 +108,6 @@ util_hash_file (const char *filename, struct GNUNET_HashCode *hash); /** - * Encrypts a file inplace under a given <i>filename</i> - * with a selected symmetric <i>key</i> and its <i>hash</i> - * as initialization vector. - * - * @param[in] filename File name - * @param[in] hash Hash of file - * @param[in] key Symmetric key - * @return #GNUNET_OK on success, otherwise #GNUNET_SYSERR - */ -enum GNUNET_GenericReturnValue -util_encrypt_file (const char *filename, - const struct GNUNET_HashCode *hash, - const struct GNUNET_CRYPTO_SymmetricSessionKey *key); - -/** - * Decrypts a file inplace under a given <i>filename</i> - * with a selected symmetric <i>key</i> and its <i>hash</i> - * as parameter for the initialization vector and comparison - * to verify success. - * - * @param[in] filename File name - * @param[in] hash Hash of file - * @param[in] key Symmetric key - * @return #GNUNET_OK on success, otherwise #GNUNET_SYSERR - */ -enum GNUNET_GenericReturnValue -util_decrypt_file (const char *filename, - const struct GNUNET_HashCode *hash, - const struct GNUNET_CRYPTO_SymmetricSessionKey *key); - -/** * Append the path of a <i>directory</i> and a custom * subdirectory name to a composed <i>filename</i>. * @@ -219,7 +188,7 @@ util_lobby_name (const struct GNUNET_HashCode *hash, char **name); /** - * Check whether an identity <i>name</i> could be a + * Check whether an identity <i>name</i> could be a * standardized name for a lobby. * @see util_lobby_name() *