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:
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;