messenger-cli

Command-line user interface for GNUnet Messenger
Log | Files | Refs | README | LICENSE

commit 1fbc0d9f724ddf223df329fafa166944bccd4dd5
parent f63f8649ae8608edb315738bc90c4596a051baa0
Author: Jacki <jacki@thejackimonster.de>
Date:   Fri, 16 Jan 2026 23:25:55 +0100

Implement using libsecret for automatic secret usage to encrypt/decrypt local keys

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

Diffstat:
Mmeson.build | 3++-
Msrc/meson.build | 3++-
Asrc/secret.c | 158+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/secret.h | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/ui/accounts.c | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
5 files changed, 304 insertions(+), 4 deletions(-)

diff --git a/meson.build b/meson.build @@ -1,6 +1,6 @@ # # This file is part of GNUnet. -# Copyright (C) 2023--2024 GNUnet e.V. +# Copyright (C) 2023--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 @@ -33,6 +33,7 @@ messenger_cli_deps = [ dependency('gnunetchat'), dependency('gnunetutil'), dependency('ncurses'), + dependency('libsecret-1'), ] messenger_cli_args = [ diff --git a/src/meson.build b/src/meson.build @@ -1,6 +1,6 @@ # # This file is part of GNUnet. -# Copyright (C) 2023 GNUnet e.V. +# Copyright (C) 2023--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 @@ -23,6 +23,7 @@ subdir('ui') messenger_cli_sources = files([ 'application.c', 'application.h', 'chat.c', 'chat.h', + 'secret.c', 'secret.h', 'util.c', 'util.h', 'messenger_cli.c', ]) + messenger_cli_ui_sources diff --git a/src/secret.c b/src/secret.c @@ -0,0 +1,158 @@ +/* + This file is part of GNUnet. + Copyright (C) 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 + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/* + * @author Tobias Frisch + * @file secret.c + */ + +#include "secret.h" + +#include <gnunet/gnunet_util_lib.h> +#include <string.h> + +#define SECRET_APP_ID "org.gnunet.Messenger" + +const SecretSchema * +_secret_schema(void) +{ + static const SecretSchema schema = { + "org.gnunet.chat.AccountSecret", SECRET_SCHEMA_NONE, + { + { "name", SECRET_SCHEMA_ATTRIBUTE_STRING }, + { "app_id", SECRET_SCHEMA_ATTRIBUTE_STRING }, + { "NULL", 0 }, + } + }; + return &schema; +} + +char* +_secret_description(const char *name) +{ + char *desc; + + GNUNET_asprintf( + &desc, + "GNUnet Messenger account secret for identity %s", + name + ); + + return desc; +} + +char* +secret_lookup(const char *name, + uint32_t *secret_len) +{ + GError *error = NULL; + gchar *password; + + password = secret_password_lookup_sync( + _secret_schema(), + NULL, + &error, + "name", name, + "app_id", SECRET_APP_ID, + NULL + ); + + if (error) + { + return NULL; + } + else if (password) + { + *secret_len = g_utf8_strlen(password, -1); + return password; + } + else + { + *secret_len = 0; + return NULL; + } +} + +bool +secret_store(const char *name, + const char *secret, + uint32_t secret_len) +{ + GError *error = NULL; + gboolean result; + + if (strlen(secret) != secret_len) + return false; + + result = secret_password_store_sync ( + _secret_schema(), + SECRET_COLLECTION_DEFAULT, + _secret_description(name), + secret, + NULL, + &error, + "name", name, + "app_id", SECRET_APP_ID, + NULL + ); + + if (error) + return false; + else + return result; +} + +bool +secret_delete(const char *name) +{ + GError *error = NULL; + gboolean result; + + result = secret_password_clear_sync( + _secret_schema(), + NULL, + &error, + "name", name, + "app_id", SECRET_APP_ID, + NULL + ); + + if (error) + return false; + else + return result; +} + +void +secret_wipe(char *secret) +{ + gchar *password = secret; + + if (password) + secret_password_wipe(password); +} + +void +secret_free(char *secret) +{ + gchar *password = secret; + + if (password) + secret_password_free(password); +} diff --git a/src/secret.h b/src/secret.h @@ -0,0 +1,85 @@ +/* + This file is part of GNUnet. + Copyright (C) 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 + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/* + * @author Tobias Frisch + * @file secret.h + */ + +#ifndef SECRET_H_ +#define SECRET_H_ + +#include <stdbool.h> +#include <stdint.h> + +#include <libsecret/secret.h> + +/** + * Lookup a secret from identity with + * a given name. + * + * @param[in] name Identity name + * @param[out] secret_len Length of secret + * @return Secret or NULL + */ +char* +secret_lookup(const char *name, + uint32_t *secret_len); + +/** + * Stores a secret for identity with + * a given name. + * + * @param[in] name Identity name + * @param[in] secret Secret + * @param[in] secret_len Length of secret + * @return Whether the storage was successful + */ +bool +secret_store(const char *name, + const char *secret, + uint32_t secret_len); + +/** + * Delete a secret from identity with + * a given name. + * + * @param[in] name Identity name + * @return Whether the deletion was successful + */ +bool +secret_delete(const char *name); + +/** + * Wipe a secret from memory. + * + * @param[out] secret + */ +void +secret_wipe(char *secret); + +/** + * Wipe and free a secret from memory. + * + * @param[out] secret + */ +void +secret_free(char *secret); + +#endif /* SECRET_H_ */ diff --git a/src/ui/accounts.c b/src/ui/accounts.c @@ -24,8 +24,12 @@ #include "accounts.h" +#include <curses.h> +#include <gnunet/gnunet_chat_lib.h> + #include "list_input.h" #include "../application.h" +#include "../secret.h" #include "../util.h" int @@ -68,9 +72,60 @@ accounts_event(UI_ACCOUNTS_Handle *accounts, case '\n': case KEY_ENTER: if (accounts->selected) - GNUNET_CHAT_connect(app->chat.handle, accounts->selected); + { + char new_secret [65]; + uint32_t secret_len; + const char *secret; + char *secret_ptr; + + const char *name = GNUNET_CHAT_account_get_name( + accounts->selected + ); + + secret_ptr = secret_lookup(name, &secret_len); + secret = secret_ptr; + + if (!secret) + { + secret_len = 64; + if (GNUNET_OK == GNUNET_CHAT_generate_secret(new_secret, secret_len)) + { + new_secret[secret_len] = '\0'; + + if (secret_store(name, new_secret, secret_len)) + secret = new_secret; + } + } + + if (!secret) + secret_len = 0; + + GNUNET_CHAT_connect( + app->chat.handle, + accounts->selected, + secret, + secret_len + ); + + if (secret_ptr) + secret_free(secret_ptr); + else + secret_wipe(new_secret); + } else - accounts->create_dialog.window = &(accounts->window); + accounts->create_dialog.window = &(accounts->window); + break; + case 8: + case KEY_BACKSPACE: + if (accounts->selected) + { + const char *name = GNUNET_CHAT_account_get_name( + accounts->selected + ); + + if (GNUNET_OK == GNUNET_CHAT_account_delete(app->chat.handle, name)) + secret_delete(name); + } break; default: break;