From d01509d9afe32c0d98d2efba5e75a9df53ac5de9 Mon Sep 17 00:00:00 2001 From: Cyrille Bagard Date: Mon, 10 Feb 2025 01:39:50 +0100 Subject: Switch the secure storage to the GObject system. --- plugins/pychrysalide/core/Makefile.am | 3 +- plugins/pychrysalide/core/module.c | 2 - plugins/pychrysalide/core/secstorage.c | 465 ------------ plugins/pychrysalide/core/secstorage.h | 39 - plugins/pychrysalide/glibext/Makefile.am | 1 + plugins/pychrysalide/glibext/module.c | 2 + plugins/pychrysalide/glibext/secstorage.c | 624 ++++++++++++++++ plugins/pychrysalide/glibext/secstorage.h | 45 ++ plugins/pychrysalide/helpers.h | 2 +- src/core/Makefile.am | 3 +- src/core/core.c | 5 +- src/core/global.c | 46 ++ src/core/global.h | 7 + src/core/secstorage.c | 779 -------------------- src/core/secstorage.h | 65 -- src/glibext/Makefile.am | 2 + src/glibext/secstorage-int.h | 56 ++ src/glibext/secstorage.c | 820 +++++++++++++++++++++ src/glibext/secstorage.h | 68 ++ .../re.chrysalide.tests.secstorage.gschema.xml | 15 - tests/core/secstorage.py | 150 ---- .../re.chrysalide.tests.secstorage.gschema.xml | 15 + tests/glibext/secstorage.py | 143 ++++ 23 files changed, 1834 insertions(+), 1523 deletions(-) delete mode 100644 plugins/pychrysalide/core/secstorage.c delete mode 100644 plugins/pychrysalide/core/secstorage.h create mode 100644 plugins/pychrysalide/glibext/secstorage.c create mode 100644 plugins/pychrysalide/glibext/secstorage.h delete mode 100644 src/core/secstorage.c delete mode 100644 src/core/secstorage.h create mode 100644 src/glibext/secstorage-int.h create mode 100644 src/glibext/secstorage.c create mode 100644 src/glibext/secstorage.h delete mode 100644 tests/core/re.chrysalide.tests.secstorage.gschema.xml delete mode 100644 tests/core/secstorage.py create mode 100644 tests/glibext/re.chrysalide.tests.secstorage.gschema.xml create mode 100644 tests/glibext/secstorage.py diff --git a/plugins/pychrysalide/core/Makefile.am b/plugins/pychrysalide/core/Makefile.am index 6ba9fc8..5588c9f 100644 --- a/plugins/pychrysalide/core/Makefile.am +++ b/plugins/pychrysalide/core/Makefile.am @@ -15,8 +15,7 @@ libpychrysacore_la_SOURCES = \ constants.h constants.c \ logs.h logs.c \ module.h module.c \ - nox.h nox.c \ - secstorage.h secstorage.c + nox.h nox.c libpychrysacore_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ -I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT diff --git a/plugins/pychrysalide/core/module.c b/plugins/pychrysalide/core/module.c index 7eceddd..4af0403 100644 --- a/plugins/pychrysalide/core/module.c +++ b/plugins/pychrysalide/core/module.c @@ -35,7 +35,6 @@ //#include "params.h" //#include "processors.h" //#include "queue.h" -#include "secstorage.h" #include "../helpers.h" @@ -111,7 +110,6 @@ bool populate_core_module(void) //if (result) result = populate_core_module_with_params(); //if (result) result = populate_core_module_with_processors(); //if (result) result = populate_core_module_with_queue(); - if (result) result = populate_core_module_with_secstorage(); assert(result); diff --git a/plugins/pychrysalide/core/secstorage.c b/plugins/pychrysalide/core/secstorage.c deleted file mode 100644 index 67779af..0000000 --- a/plugins/pychrysalide/core/secstorage.c +++ /dev/null @@ -1,465 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * secstorage.c - équivalent Python du fichier "core/secstorage.c" - * - * Copyright (C) 2025 Cyrille Bagard - * - * This file is part of Chrysalide. - * - * Chrysalide is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * Chrysalide 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include "secstorage.h" - - -#include - - -#include "../access.h" -#include "../convert.h" -#include "../helpers.h" - - - -/* Détermine si une clef de chiffrement protégée est en place. */ -static PyObject *py_secstorage_has_secret_storage_key(PyObject *, PyObject *); - -/* Définit un mot de passe pour protéger une clef maître. */ -static PyObject *py_secstorage_set_secret_storage_password(PyObject *, PyObject *); - -/* Détermine si la clef de chiffrement maître est vérouillée. */ -static PyObject *py_secstorage_is_secret_storage_locked(PyObject *, PyObject *); - -/* Déverrouille la clef de chiffrement maître. */ -static PyObject *py_secstorage_unlock_secret_storage(PyObject *, PyObject *); - -/* Verrouille la clef de chiffrement maître. */ -static PyObject *py_secstorage_lock_secret_storage(PyObject *, PyObject *); - -/* Chiffre des données avec la clef de chiffrement maître. */ -static PyObject *py_secstorage_encrypt_secret_storage_data(PyObject *, PyObject *); - -/* Déchiffre des données avec la clef de chiffrement maître. */ -static PyObject *py_secstorage_decrypt_secret_storage_data(PyObject *, PyObject *); - - - -/****************************************************************************** -* * -* Paramètres : self = objet Python concerné par l'appel. * -* args = arguments fournis à l'appel. * -* * -* Description : Détermine si un mot de passe est actuellement en place. * -* * -* Retour : Bilan de l'analyse. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_secstorage_has_secret_storage_key(PyObject *self, PyObject *args) -{ - PyObject *result; /* Conversion à retourner */ - GSettings *settings; /* Configuration à considérer */ - int ret; /* Bilan de lecture des args. */ - bool status; /* Bilan de situation */ - -#define SECSTORAGE_HAS_SECRET_STORAGE_KEY_METHOD PYTHON_METHOD_DEF \ -( \ - has_secret_storage_key, "/, settings=None", \ - METH_VARARGS, py_secstorage, \ - "Indicate if a master key used for protecting secrets seems to have"\ - " been defined.\n" \ - "\n" \ - "The *settings* arguement must point to a GSettings intance; the" \ - " main configuration settings are used by default.\n" \ - "\n" \ - "The result is a boolean status: *True* if the master key seems" \ - " to exist, *False* otherwise." \ -) - - ret = PyArg_ParseTuple(args, "O&", convert_to_gsettings, &settings); - if (!ret) return NULL; - - status = has_secret_storage_key(settings); - - result = status ? Py_True : Py_False; - Py_INCREF(result); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : self = objet Python concerné par l'appel. * -* args = arguments fournis à l'appel. * -* * -* Description : Définit un mot de passe pour protéger une clef maître. * -* * -* Retour : Bilan de la mise en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_secstorage_set_secret_storage_password(PyObject *self, PyObject *args) -{ - PyObject *result; /* Conversion à retourner */ - GSettings *settings; /* Configuration à considérer */ - const char *passwd; /* Mot de passe associé */ - int ret; /* Bilan de lecture des args. */ - bool status; /* Bilan de situation */ - -#define SECSTORAGE_SET_SECRET_STORAGE_PASSWORD_METHOD PYTHON_METHOD_DEF \ -( \ - set_secret_storage_password, "/, settings=None, password=''", \ - METH_VARARGS, py_secstorage, \ - "Create a master key used for protecting secrets. This key is" \ - " itself protected by the provided password.\n" \ - "\n" \ - "The *settings* arguement must point to a GSettings intance; the" \ - " main configuration settings are used by default. The supplied" \ - " *password* has to be a string.\n" \ - "\n" \ - "The result is a boolean status: *True* if the operation successed,"\ - " *False* otherwise." \ -) - - settings = NULL; - passwd = ""; - - ret = PyArg_ParseTuple(args, "|O&s", convert_to_gsettings, &settings, &passwd); - if (!ret) return NULL; - - status = set_secret_storage_password(settings, passwd); - - result = status ? Py_True : Py_False; - Py_INCREF(result); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : self = objet Python concerné par l'appel. * -* args = arguments fournis à l'appel. * -* * -* Description : Détermine si la clef de chiffrement maître est vérouillée. * -* * -* Retour : Bilan de la détermination. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_secstorage_is_secret_storage_locked(PyObject *self, PyObject *args) -{ - PyObject *result; /* Conversion à retourner */ - GSettings *settings; /* Configuration à considérer */ - int ret; /* Bilan de lecture des args. */ - bool status; /* Bilan de situation */ - -#define SECSTORAGE_IS_SECRET_STORAGE_LOCKED_METHOD PYTHON_METHOD_DEF \ -( \ - is_secret_storage_locked, "/, settings=None", \ - METH_VARARGS, py_secstorage, \ - "Indicate if the master key used for protecting secrets is" \ - " currently decrypted in memory.\n" \ - "\n" \ - "The *settings* arguement must point to a GSettings intance; the" \ - " main configuration settings are used by default.\n" \ - "\n" \ - "The result is a boolean status: *True* if the master key is" \ - " unlocked and ready for use, *False* otherwise." \ -) - - settings = NULL; - - ret = PyArg_ParseTuple(args, "|O&", convert_to_gsettings, &settings); - if (!ret) return NULL; - - status = is_secret_storage_locked(settings); - - result = status ? Py_True : Py_False; - Py_INCREF(result); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : self = objet Python concerné par l'appel. * -* args = arguments fournis à l'appel. * -* * -* Description : Déverrouille la clef de chiffrement maître. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_secstorage_unlock_secret_storage(PyObject *self, PyObject *args) -{ - PyObject *result; /* Conversion à retourner */ - GSettings *settings; /* Configuration à considérer */ - const char *passwd; /* Mot de passe associé */ - int ret; /* Bilan de lecture des args. */ - bool status; /* Bilan de situation */ - -#define SECSTORAGE_UNLOCK_SECRET_STORAGE_METHOD PYTHON_METHOD_DEF \ -( \ - unlock_secret_storage, "/, settings=None, password=''", \ - METH_VARARGS, py_secstorage, \ - "Decrypt in memory the master key used for protecting secrets.\n" \ - "\n" \ - "The *settings* arguement must point to a GSettings intance; the" \ - " main configuration settings are used by default. The supplied" \ - " *password* is the primary password used to protect this key.\n" \ - "\n" \ - "The result is a boolean status: *True* if the operation successed" \ - " or if the master key is already unlocked, *False* otherwise." \ -) - - settings = NULL; - passwd = ""; - - ret = PyArg_ParseTuple(args, "|O&s", convert_to_gsettings, &settings, &passwd); - if (!ret) return NULL; - - status = unlock_secret_storage(settings, passwd); - - result = status ? Py_True : Py_False; - Py_INCREF(result); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : self = objet Python concerné par l'appel. * -* args = arguments fournis à l'appel. * -* * -* Description : Verrouille la clef de chiffrement maître. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_secstorage_lock_secret_storage(PyObject *self, PyObject *args) -{ - PyObject *result; /* Conversion à retourner */ - GSettings *settings; /* Configuration à considérer */ - int ret; /* Bilan de lecture des args. */ - -#define SECSTORAGE_LOCK_SECRET_STORAGE_METHOD PYTHON_METHOD_DEF \ -( \ - lock_secret_storage, "/, settings=None", \ - METH_VARARGS, py_secstorage, \ - "Clear from memory the master key used for protecting secrets.\n" \ - "\n" \ - "The *settings* arguement must point to a GSettings intance; the" \ - " main configuration settings are used by default." \ -) - - settings = NULL; - - ret = PyArg_ParseTuple(args, "|O&", convert_to_gsettings, &settings); - if (!ret) return NULL; - - lock_secret_storage(settings); - - result = Py_None; - Py_INCREF(result); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : self = objet Python concerné par l'appel. * -* args = arguments fournis à l'appel. * -* * -* Description : Chiffre des données avec la clef de chiffrement maître. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_secstorage_encrypt_secret_storage_data(PyObject *self, PyObject *args) -{ - PyObject *result; /* Conversion à retourner */ - const char *data_in; /* Données d'entrée à chiffrer */ - Py_ssize_t size_in; /* Quantité de ces données */ - GSettings *settings; /* Configuration à considérer */ - int ret; /* Bilan de lecture des args. */ - sized_binary_t in; /* Données à chiffer */ - bool status; /* Bilan de situation */ - sized_binary_t out; /* Données chiffrées */ - -#define SECSTORAGE_ENCRYPT_SECRET_STORAGE_DATA_METHOD PYTHON_METHOD_DEF \ -( \ - encrypt_secret_storage_data, "data, /, settings=None", \ - METH_VARARGS, py_secstorage, \ - "Encrypt data using an unlocked the master key.\n" \ - "\n" \ - "The *settings* arguement must point to a GSettings intance; the" \ - " main configuration settings are used by default." \ - "\n" \ - "The result is either encrypted data as bytes in case of success," \ - " or *None* in case of failure." \ -) - - settings = NULL; - - ret = PyArg_ParseTuple(args, "s#|O&", &data_in, &size_in, convert_to_gsettings, &settings); - if (!ret) return NULL; - - in.static_data = data_in; - in.size = size_in; - - status = encrypt_secret_storage_data(settings, &in, &out); - - if (status) - { - result = PyBytes_FromStringAndSize(out.static_data, out.size); - exit_sized_binary(&out); - } - - else - { - result = Py_None; - Py_INCREF(result); - } - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : self = objet Python concerné par l'appel. * -* args = arguments fournis à l'appel. * -* * -* Description : Déchiffre des données avec la clef de chiffrement maître. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_secstorage_decrypt_secret_storage_data(PyObject *self, PyObject *args) -{ - PyObject *result; /* Conversion à retourner */ - const char *data_in; /* Données d'entrée à chiffrer */ - Py_ssize_t size_in; /* Quantité de ces données */ - GSettings *settings; /* Configuration à considérer */ - int ret; /* Bilan de lecture des args. */ - sized_binary_t in; /* Données à chiffer */ - bool status; /* Bilan de situation */ - sized_binary_t out; /* Données chiffrées */ - -#define SECSTORAGE_DECRYPT_SECRET_STORAGE_DATA_METHOD PYTHON_METHOD_DEF \ -( \ - decrypt_secret_storage_data, "data, /, settings=None", \ - METH_VARARGS, py_secstorage, \ - "Decrypt data using an unlocked the master key.\n" \ - "\n" \ - "The *settings* arguement must point to a GSettings intance; the" \ - " main configuration settings are used by default." \ - "\n" \ - "The result is either decrypted data as bytes in case of success," \ - " or *None* in case of failure." \ -) - - settings = NULL; - - ret = PyArg_ParseTuple(args, "s#|O&", &data_in, &size_in, convert_to_gsettings, &settings); - if (!ret) return NULL; - - in.static_data = data_in; - in.size = size_in; - - status = decrypt_secret_storage_data(settings, &in, &out); - - if (status) - { - result = PyBytes_FromStringAndSize(out.static_data, out.size); - exit_sized_binary(&out); - } - - else - { - result = Py_None; - Py_INCREF(result); - } - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : - * -* * -* Description : Définit une extension du module 'core' à compléter. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool populate_core_module_with_secstorage(void) -{ - bool result; /* Bilan à retourner */ - PyObject *module; /* Module à recompléter */ - - static PyMethodDef py_secstorage_methods[] = { - SECSTORAGE_HAS_SECRET_STORAGE_KEY_METHOD, - SECSTORAGE_SET_SECRET_STORAGE_PASSWORD_METHOD, - SECSTORAGE_IS_SECRET_STORAGE_LOCKED_METHOD, - SECSTORAGE_UNLOCK_SECRET_STORAGE_METHOD, - SECSTORAGE_LOCK_SECRET_STORAGE_METHOD, - SECSTORAGE_ENCRYPT_SECRET_STORAGE_DATA_METHOD, - SECSTORAGE_DECRYPT_SECRET_STORAGE_DATA_METHOD, - { NULL } - }; - - module = get_access_to_python_module("pychrysalide.core"); - - result = register_python_module_methods(module, py_secstorage_methods); - - return result; - -} diff --git a/plugins/pychrysalide/core/secstorage.h b/plugins/pychrysalide/core/secstorage.h deleted file mode 100644 index d05d052..0000000 --- a/plugins/pychrysalide/core/secstorage.h +++ /dev/null @@ -1,39 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * secstorage.h - prototypes pour l'équivalent Python du fichier "core/secstorage.h" - * - * Copyright (C) 2025 Cyrille Bagard - * - * This file is part of Chrysalide. - * - * Chrysalide is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * Chrysalide 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#ifndef _PLUGINS_PYCHRYSALIDE_CORE_SECSTORAGE_H -#define _PLUGINS_PYCHRYSALIDE_CORE_SECSTORAGE_H - - -#include -#include - - - -/* Définit une extension du module 'core' à compléter. */ -bool populate_core_module_with_secstorage(void); - - - -#endif /* _PLUGINS_PYCHRYSALIDE_CORE_SECSTORAGE_H */ diff --git a/plugins/pychrysalide/glibext/Makefile.am b/plugins/pychrysalide/glibext/Makefile.am index 2d45244..af1d9f2 100644 --- a/plugins/pychrysalide/glibext/Makefile.am +++ b/plugins/pychrysalide/glibext/Makefile.am @@ -26,6 +26,7 @@ libpychrysaglibext_la_SOURCES = \ module.h module.c \ objhole.h objhole.c \ portion.h portion.c \ + secstorage.h secstorage.c \ singleton.h singleton.c \ strbuilder.h strbuilder.c \ work.h work.c \ diff --git a/plugins/pychrysalide/glibext/module.c b/plugins/pychrysalide/glibext/module.c index 8adae07..6ce0709 100644 --- a/plugins/pychrysalide/glibext/module.c +++ b/plugins/pychrysalide/glibext/module.c @@ -43,6 +43,7 @@ #include "hashable.h" #include "objhole.h" #include "portion.h" +#include "secstorage.h" #include "singleton.h" #include "strbuilder.h" #include "work.h" @@ -121,6 +122,7 @@ bool populate_glibext_module(void) if (result) result = ensure_python_thick_object_is_registered(); if (result) result = ensure_python_binary_portion_is_registered(); if (result) result = ensure_python_generic_work_is_registered(); + if (result) result = ensure_python_secret_storage_is_registered(); if (result) result = ensure_python_singleton_factory_is_registered(); if (result) result = ensure_python_work_queue_is_registered(); diff --git a/plugins/pychrysalide/glibext/secstorage.c b/plugins/pychrysalide/glibext/secstorage.c new file mode 100644 index 0000000..b5adb7c --- /dev/null +++ b/plugins/pychrysalide/glibext/secstorage.c @@ -0,0 +1,624 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * secstorage.c - équivalent Python du fichier "glibext/secstorage.c" + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "secstorage.h" + + +#include +#include + + +#include + + +#include "../access.h" +#include "../convert.h" +#include "../helpers.h" + + + +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +CREATE_DYN_CONSTRUCTOR(secret_storage, G_TYPE_SECRET_STORAGE); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_secret_storage_init(PyObject *, PyObject *, PyObject *); + + + +/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */ + + +/* Définit un mot de passe pour protéger une clef maître. */ +static PyObject *py_secret_storage_set_password(PyObject *, PyObject *); + +/* Déverrouille la clef de chiffrement maître. */ +static PyObject *py_secret_storage_unlock(PyObject *, PyObject *); + +/* Verrouille la clef de chiffrement maître. */ +static PyObject *py_secret_storage_lock(PyObject *, PyObject *); + +/* Chiffre des données avec la clef de chiffrement maître. */ +static PyObject *py_secret_storage_encrypt_data(PyObject *, PyObject *); + +/* Déchiffre des données avec la clef de chiffrement maître. */ +static PyObject *py_secret_storage_decrypt_data(PyObject *, PyObject *); + +/* Détermine si une clef de chiffrement protégée est en place. */ +static PyObject *py_secret_storage_has_key(PyObject *, void *); + +/* Détermine si la clef de chiffrement maître est vérouillée. */ +static PyObject *py_secret_storage_is_locked(PyObject *, void *); + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_secret_storage_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + GSettings *settings; /* Configuration à considérer */ + int ret; /* Bilan de lecture des args. */ + GSecretStorage *storage; /* Stockage natif à manipuler */ + +#define SECRET_STORAGE_DOC \ + "SecretStorage acts as guardian for secrets using ecryption," \ + " mainly for sensitive information stored as configuration" \ + " parameters.\n" \ + "\n" \ + "Instances can be created using the following constructor:\n" \ + "\n" \ + " SecretStorage(settings)" \ + "\n" \ + "The *settings* arguement must point to a GSettings intance;" \ + " the main configuration settings are used by default." \ + + settings = NULL; + + ret = PyArg_ParseTuple(args, "|O&", convert_to_gsettings, &settings); + if (!ret) return -1; + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + /* Eléments de base */ + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + if (!g_secret_storage_create(storage, settings)) + return -1; + + return 0; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* CONNEXION AVEC L'API DE PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* args = arguments fournis à l'appel. * +* * +* Description : Définit un mot de passe pour protéger une clef maître. * +* * +* Retour : Bilan de la mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_set_password(PyObject *self, PyObject *args) +{ + PyObject *result; /* Conversion à retourner */ + const char *passwd; /* Mot de passe associé */ + int ret; /* Bilan de lecture des args. */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + bool status; /* Bilan de situation */ + +#define SECRET_STORAGE_SET_PASSWORD_METHOD PYTHON_METHOD_DEF \ +( \ + set_password, "/, password=''", \ + METH_VARARGS, py_secret_storage, \ + "Create a master key used for protecting secrets. This key is" \ + " itself protected by the provided password.\n" \ + "\n" \ + "The supplied *password* has to be a string.\n" \ + "\n" \ + "The result is a boolean status: *True* if the operation successed,"\ + " *False* otherwise." \ +) + + passwd = ""; + + ret = PyArg_ParseTuple(args, "|s", &passwd); + if (!ret) return NULL; + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + status = g_secret_storage_set_password(storage, passwd); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* args = arguments fournis à l'appel. * +* * +* Description : Déverrouille la clef de chiffrement maître. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_unlock(PyObject *self, PyObject *args) +{ + PyObject *result; /* Conversion à retourner */ + const char *passwd; /* Mot de passe associé */ + int ret; /* Bilan de lecture des args. */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + bool status; /* Bilan de situation */ + +#define SECRET_STORAGE_UNLOCK_METHOD PYTHON_METHOD_DEF \ +( \ + unlock, "/, password=''", \ + METH_VARARGS, py_secret_storage, \ + "Decrypt in memory the master key used for protecting secrets.\n" \ + "\n" \ + "The supplied *password* is the primary password used to protect" \ + " this key.\n" \ + "\n" \ + "The result is a boolean status: *True* if the operation successed" \ + " or if the master key is already unlocked, *False* otherwise." \ +) + + passwd = ""; + + ret = PyArg_ParseTuple(args, "|s", &passwd); + if (!ret) return NULL; + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + status = g_secret_storage_unlock(storage, passwd); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* args = arguments fournis à l'appel. * +* * +* Description : Verrouille la clef de chiffrement maître. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_lock(PyObject *self, PyObject *args) +{ + PyObject *result; /* Conversion à retourner */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + +#define SECRET_STORAGE_LOCK_METHOD PYTHON_METHOD_DEF \ +( \ + lock, "", \ + METH_NOARGS, py_secret_storage, \ + "Clear from memory the master key used for protecting secrets." \ +) + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + g_secret_storage_lock(storage); + + result = Py_None; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* args = arguments fournis à l'appel. * +* * +* Description : Chiffre des données avec la clef de chiffrement maître. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_encrypt_data(PyObject *self, PyObject *args) +{ + PyObject *result; /* Conversion à retourner */ + const char *data_in; /* Données d'entrée à chiffrer */ + Py_ssize_t size_in; /* Quantité de ces données */ + int ret; /* Bilan de lecture des args. */ + sized_binary_t in; /* Données à chiffer */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + bool status; /* Bilan de situation */ + sized_binary_t out; /* Données chiffrées */ + +#define SECRET_STORAGE_ENCRYPT_DATA_METHOD PYTHON_METHOD_DEF \ +( \ + encrypt_data, "data", \ + METH_VARARGS, py_secret_storage, \ + "Encrypt data using an unlocked the master key.\n" \ + "\n" \ + "The *data* arguement points to bytes to process." \ + "\n" \ + "The result is either encrypted *data* as bytes in case of success,"\ + " or *None* in case of failure." \ +) + + ret = PyArg_ParseTuple(args, "s#", &data_in, &size_in); + if (!ret) return NULL; + + in.static_data = data_in; + in.size = size_in; + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + status = g_secret_storage_encrypt_data(storage, &in, &out); + + if (status) + { + result = PyBytes_FromStringAndSize(out.static_data, out.size); + exit_sized_binary(&out); + } + + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* args = arguments fournis à l'appel. * +* * +* Description : Déchiffre des données avec la clef de chiffrement maître. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_decrypt_data(PyObject *self, PyObject *args) +{ + PyObject *result; /* Conversion à retourner */ + const char *data_in; /* Données d'entrée à chiffrer */ + Py_ssize_t size_in; /* Quantité de ces données */ + int ret; /* Bilan de lecture des args. */ + sized_binary_t in; /* Données à chiffer */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + bool status; /* Bilan de situation */ + sized_binary_t out; /* Données chiffrées */ + +#define SECRET_STORAGE_DECRYPT_DATA_METHOD PYTHON_METHOD_DEF \ +( \ + decrypt_data, "data", \ + METH_VARARGS, py_secret_storage, \ + "Decrypt data using an unlocked the master key.\n" \ + "\n" \ + "The *data* arguement points to bytes to process." \ + "\n" \ + "The result is either decrypted *data* as bytes in case of success,"\ + " or *None* in case of failure." \ +) + + ret = PyArg_ParseTuple(args, "s#", &data_in, &size_in); + if (!ret) return NULL; + + in.static_data = data_in; + in.size = size_in; + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + status = g_secret_storage_decrypt_data(storage, &in, &out); + + if (status) + { + result = PyBytes_FromStringAndSize(out.static_data, out.size); + exit_sized_binary(&out); + } + + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Détermine si une clef de chiffrement protégée est en place. * +* * +* Retour : Bilan de l'analyse. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_has_key(PyObject *self, void *closure) +{ + PyObject *result; /* Instance Python à retourner */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + bool status; /* Bilan de consultation */ + +#define PY_SECRET_STORAGE_HAS_KEY_ATTRIB PYTHON_HAS_DEF_FULL \ +( \ + key, py_secret_storage, \ + "Indicate if a master key used for protecting secrets seems to have"\ + " been defined. Without any unlocking attempt, the test only relies"\ + " on the length of saved master data.\n" \ + "\n" \ + "The returned status is a boolean status: *True* if the master key" \ + " seems to exist, *False* otherwise." \ +) + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + status = g_secret_storage_has_key(storage); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Détermine si la clef de chiffrement maître est vérouillée. * +* * +* Retour : Bilan de la détermination. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_is_locked(PyObject *self, void *closure) +{ + PyObject *result; /* Instance Python à retourner */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + bool status; /* Bilan de consultation */ + +#define SECRET_STORAGE_IS_LOCKED_ATTRIB PYTHON_IS_DEF_FULL \ +( \ + locked, py_secret_storage, \ + "Indicate if the master key used for protecting secrets is" \ + " currently decrypted in memory.\n" \ + "\n" \ + "The *settings* arguement must point to a GSettings intance; the" \ + " main configuration settings are used by default.\n" \ + "\n" \ + "The returned status is a boolean status: *True* if the master key" \ + " is unlocked and ready for use, *False* otherwise." \ +) + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + status = g_secret_storage_is_locked(storage); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_secret_storage_type(void) +{ + static PyMethodDef py_secret_storage_methods[] = { + SECRET_STORAGE_SET_PASSWORD_METHOD, + SECRET_STORAGE_UNLOCK_METHOD, + SECRET_STORAGE_LOCK_METHOD, + SECRET_STORAGE_ENCRYPT_DATA_METHOD, + SECRET_STORAGE_DECRYPT_DATA_METHOD, + { NULL } + }; + + static PyGetSetDef py_secret_storage_getseters[] = { + PY_SECRET_STORAGE_HAS_KEY_ATTRIB, + SECRET_STORAGE_IS_LOCKED_ATTRIB, + { NULL } + }; + + static PyTypeObject py_secret_storage_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.glibext.SecretStorage", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = SECRET_STORAGE_DOC, + + .tp_methods = py_secret_storage_methods, + .tp_getset = py_secret_storage_getseters, + + .tp_init = py_secret_storage_init, + .tp_new = py_secret_storage_new, + + }; + + return &py_secret_storage_type; + +} + + +/****************************************************************************** +* * +* Paramètres : module = module dont la définition est à compléter. * +* * +* Description : Prend en charge l'objet 'pychrysalide.glibext.SecretStorage'.* +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_secret_storage_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'SecretStorage' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_secret_storage_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.glibext"); + + dict = PyModule_GetDict(module); + + if (!register_class_for_pygobject(dict, G_TYPE_SECRET_STORAGE, type)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en gardien des secrets avec stockage. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_secret_storage(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_secret_storage_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to secret storage"); + break; + + case 1: + *((GSecretStorage **)dst) = G_SECRET_STORAGE(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/glibext/secstorage.h b/plugins/pychrysalide/glibext/secstorage.h new file mode 100644 index 0000000..68726c3 --- /dev/null +++ b/plugins/pychrysalide/glibext/secstorage.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * secstorage.h - prototypes pour l'équivalent Python du fichier "glibext/secstorage.h" + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_SECSTORAGE_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_SECSTORAGE_H + + +#include +#include + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_secret_storage_type(void); + +/* Prend en charge l'objet 'pychrysalide.glibext.SecretStorage'. */ +bool ensure_python_secret_storage_is_registered(void); + +/* Tente de convertir en gardien des secrets avec stockage. */ +int convert_to_secret_storage(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_SECSTORAGE_H */ diff --git a/plugins/pychrysalide/helpers.h b/plugins/pychrysalide/helpers.h index 0aaf976..745d013 100644 --- a/plugins/pychrysalide/helpers.h +++ b/plugins/pychrysalide/helpers.h @@ -132,7 +132,7 @@ bool register_python_module_object(PyObject *, PyTypeObject *); PYTHON_GETSET_DEF("is_" #name, base ## _is_ ## name, NULL, ATTRIB_RO doc, NULL) #define PYTHON_HAS_DEF_FULL(name, base, doc) \ - PYTHON_GETSET_DEF(#name, base ## _has_ ## name, NULL, ATTRIB_RO doc, NULL) + PYTHON_GETSET_DEF("has_" #name, base ## _has_ ## name, NULL, ATTRIB_RO doc, NULL) #define PYTHON_RAWGET_DEF_FULL(name, base, doc) \ PYTHON_GETSET_DEF(#name, base ## _ ## name, NULL, ATTRIB_RO doc, NULL) diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 906c383..17fd2bf 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -20,8 +20,7 @@ libcore4_la_SOURCES = \ logs.h logs.c \ nox.h nox.c \ nproc.h nproc.c \ - paths.h paths.c \ - secstorage.h secstorage.c + paths.h paths.c libcore4_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBSSL_CFLAGS) diff --git a/src/core/core.c b/src/core/core.c index eaf7763..8fe12f5 100644 --- a/src/core/core.c +++ b/src/core/core.c @@ -25,7 +25,6 @@ #include "global.h" -#include "secstorage.h" @@ -54,7 +53,7 @@ bool load_core_components(AvailableCoreComponent flags) if ((flags & ACC_GLOBAL_VARS) != 0 && (__loaded & ACC_GLOBAL_VARS) == 0) { - init_secret_storage(); + set_secret_storage(g_secret_storage_new(NULL)); set_work_queue(g_work_queue_new()); @@ -85,7 +84,7 @@ void unload_core_components(AvailableCoreComponent flags) { set_work_queue(NULL); - exit_secret_storage(); + set_secret_storage(NULL); __loaded &= ~ACC_GLOBAL_VARS; diff --git a/src/core/global.c b/src/core/global.c index d38656b..0275e09 100644 --- a/src/core/global.c +++ b/src/core/global.c @@ -36,6 +36,9 @@ static size_t _bytes_sent = 0; /* Gestionnaire de tâches parallèles */ static GWorkQueue *_queue = NULL; +/* Gardien des secrets avec support des stockages */ +static GSecretStorage *_storage = NULL; + /****************************************************************************** @@ -131,6 +134,49 @@ GWorkQueue *get_work_queue(void) } +/****************************************************************************** +* * +* Paramètres : queue = nouveau stockage sécurisé à mémoriser ou NULL. * +* * +* Description : Définit le stockage sécurisé principal. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void set_secret_storage(/* __steal */GSecretStorage *storage) +{ + if (_storage != NULL) + unref_object(_storage); + + _storage = storage; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit le stockage sécurisé principal. * +* * +* Retour : Gestionnaire de traitements parallèles courant. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GSecretStorage *get_secret_storage(void) +{ + ref_object(_storage); + + return _storage; + +} + + diff --git a/src/core/global.h b/src/core/global.h index b861ad8..f5d8a62 100644 --- a/src/core/global.h +++ b/src/core/global.h @@ -25,6 +25,7 @@ #define _CORE_GLOBAL_H +#include "../glibext/secstorage.h" #include "../glibext/workqueue.h" @@ -41,6 +42,12 @@ void set_work_queue(/* __steal */GWorkQueue *); /* Fournit le gestionnaire de traitements parallèles courant. */ GWorkQueue *get_work_queue(void); +/* Définit le stockage sécurisé principal. */ +void set_secret_storage(/* __steal */GSecretStorage *); + +/* Fournit le stockage sécurisé principal. */ +GSecretStorage *get_secret_storage(void); + diff --git a/src/core/secstorage.c b/src/core/secstorage.c deleted file mode 100644 index 7f57b1c..0000000 --- a/src/core/secstorage.c +++ /dev/null @@ -1,779 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * secstorage.c - conservation sécurisée d'éléments de configuration - * - * Copyright (C) 2025 Cyrille Bagard - * - * This file is part of Chrysalide. - * - * Chrysalide is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * Chrysalide 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Chrysalide. If not, see . - */ - - -#include "secstorage.h" - - -#include -#include -#include -#include - - -#include "../core/logs.h" -#include "../glibext/helpers.h" - - - -/** - * Les mécanismes de hachage de mot de passe doivent être utilisés avec un sel, - * et la longueur du sel doit être d’au moins 128 bits. - * - * Cette note concerne le hachage de mots de passe et non la dérivation de secrets - * cependant. - * - * Source : https://cyber.gouv.fr/sites/default/files/2021/03/anssi-guide-selection_crypto-1.0.pdf - */ - -#define SECRET_STORAGE_SALT_SIZE (256 / 8) - - -/** - * Nombre d'itérations pour PBKDF2 : en 2023, OWASP recommande 600000 itérations - * pour PBKDF2-HMAC-SHA256 (et 210000 pour PBKDF2-HMAC-SHA512). - * - * Source : https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2 - */ - -#define PBKDF2_HMAC_SHA256_ITERATIONS (2 << 20) - - -/** - * AES 256 : clef de 256 bits, IV de 128 bits - */ - -#define SECRET_STORAGE_KEK_SIZE (256 / 8) - -#define SECRET_STORAGE_KEY_SIZE (256 / 8) - -#define SECRET_STORAGE_BLOCK_SIZE (128 / 8) - -#define SECRET_STORAGE_IV_SIZE SECRET_STORAGE_BLOCK_SIZE - - -/* Conservation des clefs de déchiffrement maîtres par configuration. */ -static GHashTable *__unlocked_keys = NULL; - - -/* Fournit l'espace de configuration réel à manipuler. */ -static GSettings *get_secret_storage_settings(GSettings *); - - - -/****************************************************************************** -* * -* Paramètres : - * -* * -* Description : Initialise le stockage des clefs de déchiffrement en place. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void init_secret_storage(void) -{ - __unlocked_keys = g_hash_table_new_full(g_direct_hash, g_direct_equal, g_object_unref, free); - -} - - -/****************************************************************************** -* * -* Paramètres : - * -* * -* Description : Supprime le stockage des clefs de déchiffrement en place. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void exit_secret_storage(void) -{ - assert(__unlocked_keys != NULL); - - g_hash_table_remove_all(__unlocked_keys); - g_hash_table_unref(__unlocked_keys); - - __unlocked_keys = NULL; - -} - - -/****************************************************************************** -* * -* Paramètres : settings = éventuel espace de configuration à manipuler. * -* * -* Description : Fournit l'espace de configuration réel à manipuler. * -* * -* Retour : Instance de travail à employer avant libération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static GSettings *get_secret_storage_settings(GSettings *settings) -{ - GSettings *result; /* Instance à retourner */ - - - if (settings != NULL) - { - ref_object(settings); - result = settings; - } - else - result = NULL; // TODO - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : settings = éventuel espace de configuration à manipuler. * -* * -* Description : Détermine si une clef de chiffrement protégée est en place. * -* * -* Retour : Bilan de l'analyse. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool has_secret_storage_key(GSettings *settings) -{ - bool result; /* Bilan à retourner */ - GVariant *value; /* Valeur de configuration */ - gsize length; /* Taille d'une valeur donnée */ - - result = false; - - settings = get_secret_storage_settings(settings); - assert(settings != NULL); - - value = g_settings_get_value(settings, "master"); - - g_variant_get_fixed_array(value, &length, 1); - - result = (length > SECRET_STORAGE_IV_SIZE); - - g_variant_unref(value); - - unref_object(settings); - - return result;; - -} - - -/****************************************************************************** -* * -* Paramètres : settings = éventuel espace de configuration à manipuler. * -* password = mot de passe principal à appliquer. * -* * -* Description : Définit un mot de passe pour protéger une clef maître. * -* * -* Retour : Bilan de la mise en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool set_secret_storage_password(GSettings *settings, const char *passwd) -{ - bool result; /* Bilan à retourner */ - unsigned char salt[SECRET_STORAGE_SALT_SIZE]; /* Sel pour la dérivation*/ - int ret; /* Bilan à d'un appel */ - GVariant *value; /* Valeur de configuration */ - unsigned char kek[SECRET_STORAGE_KEK_SIZE]; /* Clef de protection */ - unsigned char key[SECRET_STORAGE_KEY_SIZE]; /* Clef maître */ - unsigned char iv[SECRET_STORAGE_IV_SIZE]; /* IV associé */ - EVP_CIPHER_CTX *ctx; /* Contexte pour le chiffrement*/ - unsigned char encrypted[64]; /* Partie chiffrée à conserver */ - unsigned char *iter; /* Tête d'écriture */ - int outlen; /* Taille des données utiles */ - - result = false; - - settings = get_secret_storage_settings(settings); - assert(settings != NULL); - - if (has_secret_storage_key(settings)) - goto exit; - - /* Création d'un sel pour la dérivation du mot de passe */ - - ret = RAND_bytes(salt, sizeof(salt)); - if (ret != 1) - { - LOG_ERROR_OPENSSL; - goto exit; - } - - /** - * La fonction g_variant_new_fixed_array() retourne un variant - * avec un décompte de référence flottant. - */ - - value = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, - salt, SECRET_STORAGE_SALT_SIZE, sizeof(unsigned char)); - - /** - * Comme le variant à une référence flottante, la fonction - * g_settings_set_value() consomme cette référence. - * - * Il n'y a donc pas lieu d'appeler g_variant_unref(). - */ - - g_settings_set_value(settings, "salt", value); - - /* Dérivation du mot de passe */ - - ret = PKCS5_PBKDF2_HMAC(passwd, strlen(passwd), - salt, SECRET_STORAGE_SALT_SIZE, - PBKDF2_HMAC_SHA256_ITERATIONS, EVP_sha256(), - SECRET_STORAGE_KEK_SIZE, kek); - - if (ret != 1) - { - LOG_ERROR_OPENSSL; - goto exit; - } - - /* Définition de la clef maître et de son IV de chiffrement */ - - ret = RAND_bytes(key, sizeof(key)); - if (ret != 1) - { - LOG_ERROR_OPENSSL; - goto exit; - } - - ret = RAND_bytes(iv, sizeof(iv)); - if (ret != 1) - { - LOG_ERROR_OPENSSL; - goto exit; - } - - /* Chiffrement de la clef maître */ - - ctx = EVP_CIPHER_CTX_new(); - - if (ctx == NULL) - { - LOG_ERROR_OPENSSL; - goto exit; - } - - EVP_CIPHER_CTX_set_flags(ctx, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); - - ret = EVP_EncryptInit_ex2(ctx, EVP_aes_256_wrap_pad(), kek, iv, NULL); - if (ret != 1) - { - LOG_ERROR_OPENSSL; - goto exit_with_ctx; - } - - memcpy(encrypted, iv, SECRET_STORAGE_IV_SIZE); - - iter = encrypted + SECRET_STORAGE_IV_SIZE; - - ret = EVP_EncryptUpdate(ctx, iter, &outlen, key, SECRET_STORAGE_KEY_SIZE); - if (ret != 1) - { - LOG_ERROR_OPENSSL; - goto exit_with_ctx; - } - - iter += outlen; - - ret = EVP_EncryptFinal_ex(ctx, iter, &outlen); - if (ret != 1) - { - LOG_ERROR_OPENSSL; - goto exit_with_ctx; - } - - iter += outlen; - - assert((iter - encrypted) < 64); - - /* Conservation de la clef protégée */ - - value = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, - encrypted, iter - encrypted, sizeof(unsigned char)); - - g_settings_set_value(settings, "master", value); - - result = true; - - exit_with_ctx: - - EVP_CIPHER_CTX_free(ctx); - - exit: - - unref_object(settings); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : settings = éventuel espace de configuration à manipuler. * -* * -* Description : Détermine si la clef de chiffrement maître est vérouillée. * -* * -* Retour : Bilan de la détermination. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool is_secret_storage_locked(GSettings *settings) -{ - bool result; /* Bilan à retourner */ - - settings = get_secret_storage_settings(settings); - assert(settings != NULL); - - result = (g_hash_table_lookup(__unlocked_keys, settings) == NULL); - - unref_object(settings); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : settings = éventuel espace de configuration à manipuler. * -* password = mot de passe principal à utiliser. * -* * -* Description : Déverrouille la clef de chiffrement maître. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool unlock_secret_storage(GSettings *settings, const char *passwd) -{ - bool result; /* Bilan à retourner */ - GVariant *salt_value; /* Valeur du sel configuré */ - gsize salt_length; /* Taille du sel conservé */ - gconstpointer salt; /* Données associées #1 */ - unsigned char kek[SECRET_STORAGE_KEK_SIZE]; /* Clef de protection */ - int ret; /* Bilan à d'un appel */ - GVariant *enc_value; /* Paramètres de chiffrement */ - gsize enc_length; /* Taille de ces paramètrs */ - gconstpointer encrypted; /* Données associées #2 */ - EVP_CIPHER_CTX *ctx; /* Contexte de déchiffrement */ - unsigned char iv[SECRET_STORAGE_IV_SIZE]; /* IV associé */ - unsigned char key[SECRET_STORAGE_KEY_SIZE]; /* Clef maître */ - unsigned char *iter; /* Tête d'écriture */ - int outlen; /* Taille des données utiles */ - void *unlocked; /* Zone de conservation */ -#ifndef NDEBUG - gboolean new; /* Validation de la création */ -#endif - - result = false; - - settings = get_secret_storage_settings(settings); - assert(settings != NULL); - - if (!is_secret_storage_locked(settings)) - { - result = true; - goto quick_exit; - } - - /* Récupération du sel mis en place */ - - salt_value = g_settings_get_value(settings, "salt"); - - salt = g_variant_get_fixed_array(salt_value, &salt_length, sizeof(bin_t)); - - if (salt_length != SECRET_STORAGE_SALT_SIZE) - goto exit_with_salt; - - /* Dérivation du mot de passe */ - - ret = PKCS5_PBKDF2_HMAC(passwd, strlen(passwd), - salt, SECRET_STORAGE_SALT_SIZE, - PBKDF2_HMAC_SHA256_ITERATIONS, EVP_sha256(), - SECRET_STORAGE_KEK_SIZE, kek); - - if (ret != 1) - { - LOG_ERROR_OPENSSL; - goto exit_with_salt; - } - - /* Récupération des paramètres chiffrés */ - - enc_value = g_settings_get_value(settings, "master"); - - encrypted = g_variant_get_fixed_array(enc_value, &enc_length, sizeof(bin_t)); - - if (enc_length <= SECRET_STORAGE_IV_SIZE) - goto exit_with_enc; - - /* Déhiffrement de la clef maître */ - - ctx = EVP_CIPHER_CTX_new(); - - if (ctx == NULL) - { - LOG_ERROR_OPENSSL; - goto exit_with_enc; - } - - EVP_CIPHER_CTX_set_flags(ctx, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); - - memcpy(iv, encrypted, SECRET_STORAGE_IV_SIZE); - - ret = EVP_DecryptInit_ex2(ctx, EVP_aes_256_wrap_pad(), kek, iv, NULL); - if (ret != 1) - { - LOG_ERROR_OPENSSL; - goto exit_with_ctx; - } - - iter = key; - - ret = EVP_DecryptUpdate(ctx, iter, &outlen, - ((unsigned char *)encrypted) + SECRET_STORAGE_IV_SIZE, - enc_length - SECRET_STORAGE_IV_SIZE); - if (ret != 1) - { - LOG_ERROR_OPENSSL; - goto exit_with_ctx; - } - - iter += outlen; - - ret = EVP_DecryptFinal_ex(ctx, iter, &outlen); - if (ret != 1) - { - LOG_ERROR_OPENSSL; - goto exit_with_ctx; - } - - assert((iter - key) == SECRET_STORAGE_KEY_SIZE); - - /* Stockage de la clef maître en mémoire */ - - ref_object(settings); - - unlocked = malloc(SECRET_STORAGE_KEY_SIZE); - memcpy(unlocked, key, SECRET_STORAGE_KEY_SIZE); - -#ifndef NDEBUG - new = g_hash_table_replace(__unlocked_keys, settings, unlocked); - assert(new); -#else - g_hash_table_replace(__unlocked_keys, settings, unlocked); -#endif - - result = true; - - /* Sortie */ - - exit_with_ctx: - - EVP_CIPHER_CTX_free(ctx); - - exit_with_enc: - - g_variant_unref(enc_value); - - exit_with_salt: - - g_variant_unref(salt_value); - - quick_exit: - - unref_object(settings); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : settings = éventuel espace de configuration à manipuler. * -* * -* Description : Verrouille la clef de chiffrement maître. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void lock_secret_storage(GSettings *settings) -{ - settings = get_secret_storage_settings(settings); - assert(settings != NULL); - - g_hash_table_remove(__unlocked_keys, settings); - - unref_object(settings); - -} - - -/****************************************************************************** -* * -* Paramètres : settings = éventuel espace de configuration à manipuler. * -* in = séquence d'octets à traiter. * -* out = séquence d'octets résultantes. [OUT] * -* * -* Description : Chiffre des données avec la clef de chiffrement maître. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool encrypt_secret_storage_data(GSettings *settings, const sized_binary_t *in, sized_binary_t *out) -{ - bool result; /* Bilan à retourner */ - gpointer key; /* Clef de chiffrement */ - unsigned char iv[SECRET_STORAGE_IV_SIZE]; /* IV associé */ - int ret; /* Bilan à d'un appel */ - EVP_CIPHER_CTX *ctx; /* Contexte pour le chiffrement*/ - size_t needed; /* Taille de la sortie */ - unsigned char *iter; /* Tête d'écriture */ - int outlen; /* Taille des données utiles */ - - result = false; - - settings = get_secret_storage_settings(settings); - assert(settings != NULL); - - if (is_secret_storage_locked(settings)) - goto quick_exit; - - /* Récupération de la clef maître et d'un IV de chiffrement */ - - key = g_hash_table_lookup(__unlocked_keys, settings); - - ret = RAND_bytes(iv, sizeof(iv)); - if (ret != 1) - { - LOG_ERROR_OPENSSL; - goto exit; - } - - /* Préparation de la zone de réception */ - - needed = SECRET_STORAGE_IV_SIZE + ((in->size / SECRET_STORAGE_BLOCK_SIZE) + 1) * SECRET_STORAGE_BLOCK_SIZE; - - setup_sized_binary(out, needed); - - /* Chiffrement des données */ - - ctx = EVP_CIPHER_CTX_new(); - - if (ctx == NULL) - { - LOG_ERROR_OPENSSL; - goto exit; - } - - ret = EVP_EncryptInit_ex2(ctx, EVP_aes_256_cbc(), key, iv, NULL); - if (ret != 1) - { - LOG_ERROR_OPENSSL; - goto exit_with_ctx; - } - - memcpy(out->data, iv, SECRET_STORAGE_IV_SIZE); - - iter = out->bin_data + SECRET_STORAGE_IV_SIZE; - - ret = EVP_EncryptUpdate(ctx, iter, &outlen, in->bin_data, in->size); - if (ret != 1) - { - LOG_ERROR_OPENSSL; - goto exit_with_ctx; - } - - iter += outlen; - - ret = EVP_EncryptFinal_ex(ctx, iter, &outlen); - if (ret != 1) - { - LOG_ERROR_OPENSSL; - goto exit_with_ctx; - } - - iter += outlen; - - assert((iter - out->bin_data) == out->size); - - result = true; - - /* Sortie */ - - exit_with_ctx: - - EVP_CIPHER_CTX_free(ctx); - - if (!result) - exit_sized_binary(out); - - exit: - quick_exit: - - unref_object(settings); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : settings = éventuel espace de configuration à manipuler. * -* in = séquence d'octets à traiter. * -* out = séquence d'octets résultantes. [OUT] * -* * -* Description : Déchiffre des données avec la clef de chiffrement maître. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool decrypt_secret_storage_data(GSettings *settings, const sized_binary_t *in, sized_binary_t *out) -{ - bool result; /* Bilan à retourner */ - gpointer key; /* Clef de chiffrement */ - unsigned char iv[SECRET_STORAGE_IV_SIZE]; /* IV associé */ - int ret; /* Bilan à d'un appel */ - EVP_CIPHER_CTX *ctx; /* Contexte pour le chiffrement*/ - size_t needed; /* Taille de la sortie */ - unsigned char *iter; /* Tête d'écriture */ - int outlen; /* Taille des données utiles */ - - result = false; - - settings = get_secret_storage_settings(settings); - assert(settings != NULL); - - if (is_secret_storage_locked(settings)) - goto quick_exit; - - /* Récupération de la clef maître et d'un IV de chiffrement */ - - key = g_hash_table_lookup(__unlocked_keys, settings); - - if (in->size < SECRET_STORAGE_IV_SIZE) - goto exit; - - memcpy(iv, in->data, SECRET_STORAGE_IV_SIZE); - - /* Préparation de la zone de réception */ - - needed = in->size - SECRET_STORAGE_IV_SIZE; - - setup_sized_binary(out, needed); - - /* Chiffrement des données */ - - ctx = EVP_CIPHER_CTX_new(); - - if (ctx == NULL) - { - LOG_ERROR_OPENSSL; - goto exit; - } - - ret = EVP_DecryptInit_ex2(ctx, EVP_aes_256_cbc(), key, iv, NULL); - if (ret != 1) - { - LOG_ERROR_OPENSSL; - goto exit_with_ctx; - } - - iter = out->bin_data; - - ret = EVP_DecryptUpdate(ctx, iter, &outlen, - in->bin_data + SECRET_STORAGE_IV_SIZE, in->size - SECRET_STORAGE_IV_SIZE); - if (ret != 1) - { - LOG_ERROR_OPENSSL; - goto exit_with_ctx; - } - - iter += outlen; - - ret = EVP_DecryptFinal_ex(ctx, iter, &outlen); - if (ret != 1) - { - LOG_ERROR_OPENSSL; - goto exit_with_ctx; - } - - iter += outlen; - - assert((iter - out->bin_data) <= out->size); - - resize_sized_binary(out, iter - out->bin_data); - - result = true; - - /* Sortie */ - - exit_with_ctx: - - EVP_CIPHER_CTX_free(ctx); - - if (!result) - exit_sized_binary(out); - - exit: - quick_exit: - - unref_object(settings); - - return result; - -} diff --git a/src/core/secstorage.h b/src/core/secstorage.h deleted file mode 100644 index 7c27e07..0000000 --- a/src/core/secstorage.h +++ /dev/null @@ -1,65 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * secstorage.h - prototypes pour la conservation sécurisée d'éléments de configuration - * - * Copyright (C) 2025 Cyrille Bagard - * - * This file is part of Chrysalide. - * - * Chrysalide is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * Chrysalide 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Chrysalide. If not, see . - */ - - -#ifndef _CORE_SECSTORAGE_H -#define _CORE_SECSTORAGE_H - - -#include -#include - - -#include "../common/szbin.h" - - - -/* Initialise le stockage des clefs de déchiffrement en place. */ -void init_secret_storage(void); - -/* Supprime le stockage des clefs de déchiffrement en place. */ -void exit_secret_storage(void); - -/* Détermine si une clef de chiffrement protégée est en place. */ -bool has_secret_storage_key(GSettings *); - -/* Définit un mot de passe pour protéger une clef maître. */ -bool set_secret_storage_password(GSettings *, const char *); - -/* Détermine si la clef de chiffrement maître est vérouillée. */ -bool is_secret_storage_locked(GSettings *); - -/* Déverrouille la clef de chiffrement maître. */ -bool unlock_secret_storage(GSettings *, const char *); - -/* Verrouille la clef de chiffrement maître. */ -void lock_secret_storage(GSettings *); - -/* Chiffre des données avec la clef de chiffrement maître. */ -bool encrypt_secret_storage_data(GSettings *, const sized_binary_t *, sized_binary_t *); - -/* Déchiffre des données avec la clef de chiffrement maître. */ -bool decrypt_secret_storage_data(GSettings *, const sized_binary_t *, sized_binary_t *); - - - -#endif /* _CORE_SECSTORAGE_H */ diff --git a/src/glibext/Makefile.am b/src/glibext/Makefile.am index 2e9e86d..81d13d2 100644 --- a/src/glibext/Makefile.am +++ b/src/glibext/Makefile.am @@ -47,6 +47,8 @@ libglibext_la_SOURCES = \ objhole.h objhole.c \ portion-int.h \ portion.h portion.c \ + secstorage-int.h \ + secstorage.h secstorage.c \ singleton-int.h \ singleton.h singleton.c \ strbuilder-int.h \ diff --git a/src/glibext/secstorage-int.h b/src/glibext/secstorage-int.h new file mode 100644 index 0000000..310fbbc --- /dev/null +++ b/src/glibext/secstorage-int.h @@ -0,0 +1,56 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * secstorage-int.h - définitions internes pour la conservation sécurisée d'éléments de configuration + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see . + */ + + +#ifndef _GLIBEXT_SECSTORAGE_INT_H +#define _GLIBEXT_SECSTORAGE_INT_H + + +#include "secstorage.h" + + + +/* Gardien des secrets avec support des stockages (instance) */ +struct _GSecretStorage +{ + GObject parent; /* A laisser en premier */ + + GSettings *settings; /* Configuration sollicitée */ + + void *master_key; /* Clef déverrouillée */ + +}; + +/* Gardien des secrets avec support des stockages (classe) */ +struct _GSecretStorageClass +{ + GObjectClass parent; /* A laisser en premier */ + +}; + + +/* Met en place un gardien des secrets avec support de stockage. */ +bool g_secret_storage_create(GSecretStorage *, GSettings *); + + + +#endif /* _GLIBEXT_SECSTORAGE_INT_H */ diff --git a/src/glibext/secstorage.c b/src/glibext/secstorage.c new file mode 100644 index 0000000..84fc10c --- /dev/null +++ b/src/glibext/secstorage.c @@ -0,0 +1,820 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * secstorage.c - conservation sécurisée d'éléments de configuration + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see . + */ + + +#include "secstorage.h" + + +#include +#include +#include +#include + + +#include "secstorage-int.h" +#include "../core/logs.h" + + + +/** + * Les mécanismes de hachage de mot de passe doivent être utilisés avec un sel, + * et la longueur du sel doit être d’au moins 128 bits. + * + * Cette note concerne le hachage de mots de passe et non la dérivation de secrets + * cependant. + * + * Source : https://cyber.gouv.fr/sites/default/files/2021/03/anssi-guide-selection_crypto-1.0.pdf + */ + +#define SECRET_STORAGE_SALT_SIZE (256 / 8) + + +/** + * Nombre d'itérations pour PBKDF2 : en 2023, OWASP recommande 600000 itérations + * pour PBKDF2-HMAC-SHA256 (et 210000 pour PBKDF2-HMAC-SHA512). + * + * Source : https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2 + */ + +#define PBKDF2_HMAC_SHA256_ITERATIONS (2 << 20) + + +/** + * AES 256 : clef de 256 bits, IV de 128 bits + */ + +#define SECRET_STORAGE_KEK_SIZE (256 / 8) + +#define SECRET_STORAGE_KEY_SIZE (256 / 8) + +#define SECRET_STORAGE_BLOCK_SIZE (128 / 8) + +#define SECRET_STORAGE_IV_SIZE SECRET_STORAGE_BLOCK_SIZE + + + +/* Initialise la classe des stockages de secrets. */ +static void g_secret_storage_class_init(GSecretStorageClass *); + +/* Initialise une instance de stockage de secrets. */ +static void g_secret_storage_init(GSecretStorage *); + +/* Supprime toutes les références externes. */ +static void g_secret_storage_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void g_secret_storage_finalize(GObject *); + + + +/* Indique le type défini pour un gardien des secrets avec support des stockages. */ +G_DEFINE_TYPE(GSecretStorage, g_secret_storage, G_TYPE_OBJECT); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des stockages de secrets. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_secret_storage_class_init(GSecretStorageClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = g_secret_storage_dispose; + object->finalize = g_secret_storage_finalize; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = instance à initialiser. * +* * +* Description : Initialise une instance de stockage de secrets. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_secret_storage_init(GSecretStorage *storage) +{ + storage->settings = NULL; + + storage->master_key = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_secret_storage_dispose(GObject *object) +{ + GSecretStorage *storage; /* Gestion de stockage sécurisé*/ + + storage = G_SECRET_STORAGE(object); + + g_clear_object(&storage->settings); + + G_OBJECT_CLASS(g_secret_storage_parent_class)->dispose(object); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_secret_storage_finalize(GObject *object) +{ + GSecretStorage *storage; /* Gestion de stockage sécurisé*/ + + storage = G_SECRET_STORAGE(object); + + if (storage->master_key != NULL) + free(storage->master_key); + + G_OBJECT_CLASS(g_secret_storage_parent_class)->finalize(object); + +} + + +/****************************************************************************** +* * +* Paramètres : settings = éventuel espace de configuration à utiliser. * +* * +* Description : Créé un nouveau gardien des secrets avec support de stockage.* +* * +* Retour : Gestionnaire de stockage sécurisé mis en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GSecretStorage *g_secret_storage_new(GSettings *settings) +{ + GSecretStorage *result; /* Instance à retourner */ + + result = g_object_new(G_TYPE_SECRET_STORAGE, NULL); + + if (!g_secret_storage_create(result, settings)) + g_clear_object(&result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = stockage sécurisé à initialiser. * +* settings = éventuel espace de configuration à utiliser. * +* * +* Description : Met en place un gardien des secrets avec support de stockage.* +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_create(GSecretStorage *storage, GSettings *settings) +{ + bool result; /* Bilan à retourner */ + + result = true; + + if (settings != NULL) + { + ref_object(settings); + storage->settings = settings; + } + else + result = false; // TODO + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à consulter. * +* * +* Description : Détermine si une clef de chiffrement protégée est en place. * +* * +* Retour : Bilan de l'analyse. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_has_key(const GSecretStorage *storage) +{ + bool result; /* Bilan à retourner */ + GVariant *value; /* Valeur de configuration */ + gsize length; /* Taille d'une valeur donnée */ + + result = false; + + value = g_settings_get_value(storage->settings, "master"); + + g_variant_get_fixed_array(value, &length, 1); + + result = (length > SECRET_STORAGE_IV_SIZE); + + g_variant_unref(value); + + return result;; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à consulter. * +* password = mot de passe principal à appliquer. * +* * +* Description : Définit un mot de passe pour protéger une clef maître. * +* * +* Retour : Bilan de la mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_set_password(const GSecretStorage *storage, const char *passwd) +{ + bool result; /* Bilan à retourner */ + unsigned char salt[SECRET_STORAGE_SALT_SIZE]; /* Sel pour la dérivation*/ + int ret; /* Bilan à d'un appel */ + GVariant *value; /* Valeur de configuration */ + unsigned char kek[SECRET_STORAGE_KEK_SIZE]; /* Clef de protection */ + unsigned char key[SECRET_STORAGE_KEY_SIZE]; /* Clef maître */ + unsigned char iv[SECRET_STORAGE_IV_SIZE]; /* IV associé */ + EVP_CIPHER_CTX *ctx; /* Contexte pour le chiffrement*/ + unsigned char encrypted[64]; /* Partie chiffrée à conserver */ + unsigned char *iter; /* Tête d'écriture */ + int outlen; /* Taille des données utiles */ + + result = false; + + if (g_secret_storage_has_key(storage)) + goto exit; + + /* Création d'un sel pour la dérivation du mot de passe */ + + ret = RAND_bytes(salt, sizeof(salt)); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit; + } + + /** + * La fonction g_variant_new_fixed_array() retourne un variant + * avec un décompte de référence flottant. + */ + + value = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, + salt, SECRET_STORAGE_SALT_SIZE, sizeof(unsigned char)); + + /** + * Comme le variant à une référence flottante, la fonction + * g_settings_set_value() consomme cette référence. + * + * Il n'y a donc pas lieu d'appeler g_variant_unref(). + */ + + g_settings_set_value(storage->settings, "salt", value); + + /* Dérivation du mot de passe */ + + ret = PKCS5_PBKDF2_HMAC(passwd, strlen(passwd), + salt, SECRET_STORAGE_SALT_SIZE, + PBKDF2_HMAC_SHA256_ITERATIONS, EVP_sha256(), + SECRET_STORAGE_KEK_SIZE, kek); + + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit; + } + + /* Définition de la clef maître et de son IV de chiffrement */ + + ret = RAND_bytes(key, sizeof(key)); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit; + } + + ret = RAND_bytes(iv, sizeof(iv)); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit; + } + + /* Chiffrement de la clef maître */ + + ctx = EVP_CIPHER_CTX_new(); + + if (ctx == NULL) + { + LOG_ERROR_OPENSSL; + goto exit; + } + + EVP_CIPHER_CTX_set_flags(ctx, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); + + ret = EVP_EncryptInit_ex2(ctx, EVP_aes_256_wrap_pad(), kek, iv, NULL); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + memcpy(encrypted, iv, SECRET_STORAGE_IV_SIZE); + + iter = encrypted + SECRET_STORAGE_IV_SIZE; + + ret = EVP_EncryptUpdate(ctx, iter, &outlen, key, SECRET_STORAGE_KEY_SIZE); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter += outlen; + + ret = EVP_EncryptFinal_ex(ctx, iter, &outlen); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter += outlen; + + assert((iter - encrypted) < 64); + + /* Conservation de la clef protégée */ + + value = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, + encrypted, iter - encrypted, sizeof(unsigned char)); + + g_settings_set_value(storage->settings, "master", value); + + result = true; + + exit_with_ctx: + + EVP_CIPHER_CTX_free(ctx); + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à consulter. * +* * +* Description : Détermine si la clef de chiffrement maître est vérouillée. * +* * +* Retour : Bilan de la détermination. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_is_locked(const GSecretStorage *storage) +{ + bool result; /* Bilan à retourner */ + + result = (storage->master_key == NULL); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à manipuler. * +* password = mot de passe principal à utiliser. * +* * +* Description : Déverrouille la clef de chiffrement maître. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_unlock(GSecretStorage *storage, const char *passwd) +{ + bool result; /* Bilan à retourner */ + GVariant *salt_value; /* Valeur du sel configuré */ + gsize salt_length; /* Taille du sel conservé */ + gconstpointer salt; /* Données associées #1 */ + unsigned char kek[SECRET_STORAGE_KEK_SIZE]; /* Clef de protection */ + int ret; /* Bilan à d'un appel */ + GVariant *enc_value; /* Paramètres de chiffrement */ + gsize enc_length; /* Taille de ces paramètrs */ + gconstpointer encrypted; /* Données associées #2 */ + EVP_CIPHER_CTX *ctx; /* Contexte de déchiffrement */ + unsigned char iv[SECRET_STORAGE_IV_SIZE]; /* IV associé */ + unsigned char key[SECRET_STORAGE_KEY_SIZE]; /* Clef maître */ + unsigned char *iter; /* Tête d'écriture */ + int outlen; /* Taille des données utiles */ + + result = false; + + if (!g_secret_storage_is_locked(storage)) + { + result = true; + goto quick_exit; + } + + /* Récupération du sel mis en place */ + + salt_value = g_settings_get_value(storage->settings, "salt"); + + salt = g_variant_get_fixed_array(salt_value, &salt_length, sizeof(bin_t)); + + if (salt_length != SECRET_STORAGE_SALT_SIZE) + goto exit_with_salt; + + /* Dérivation du mot de passe */ + + ret = PKCS5_PBKDF2_HMAC(passwd, strlen(passwd), + salt, SECRET_STORAGE_SALT_SIZE, + PBKDF2_HMAC_SHA256_ITERATIONS, EVP_sha256(), + SECRET_STORAGE_KEK_SIZE, kek); + + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_salt; + } + + /* Récupération des paramètres chiffrés */ + + enc_value = g_settings_get_value(storage->settings, "master"); + + encrypted = g_variant_get_fixed_array(enc_value, &enc_length, sizeof(bin_t)); + + if (enc_length <= SECRET_STORAGE_IV_SIZE) + goto exit_with_enc; + + /* Déhiffrement de la clef maître */ + + ctx = EVP_CIPHER_CTX_new(); + + if (ctx == NULL) + { + LOG_ERROR_OPENSSL; + goto exit_with_enc; + } + + EVP_CIPHER_CTX_set_flags(ctx, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); + + memcpy(iv, encrypted, SECRET_STORAGE_IV_SIZE); + + ret = EVP_DecryptInit_ex2(ctx, EVP_aes_256_wrap_pad(), kek, iv, NULL); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter = key; + + ret = EVP_DecryptUpdate(ctx, iter, &outlen, + ((unsigned char *)encrypted) + SECRET_STORAGE_IV_SIZE, + enc_length - SECRET_STORAGE_IV_SIZE); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter += outlen; + + ret = EVP_DecryptFinal_ex(ctx, iter, &outlen); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + assert((iter - key) == SECRET_STORAGE_KEY_SIZE); + + /* Stockage de la clef maître en mémoire */ + + storage->master_key = malloc(SECRET_STORAGE_KEY_SIZE); + memcpy(storage->master_key, key, SECRET_STORAGE_KEY_SIZE); + + result = true; + + /* Sortie */ + + exit_with_ctx: + + EVP_CIPHER_CTX_free(ctx); + + exit_with_enc: + + g_variant_unref(enc_value); + + exit_with_salt: + + g_variant_unref(salt_value); + + quick_exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à manipuler. * +* * +* Description : Verrouille la clef de chiffrement maître. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_secret_storage_lock(GSecretStorage *storage) +{ + if (storage->master_key != NULL) + { + free(storage->master_key); + storage->master_key = NULL; + } + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à consulter. * +* in = séquence d'octets à traiter. * +* out = séquence d'octets résultantes. [OUT] * +* * +* Description : Chiffre des données avec la clef de chiffrement maître. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_encrypt_data(const GSecretStorage *storage, const sized_binary_t *in, sized_binary_t *out) +{ + bool result; /* Bilan à retourner */ + unsigned char iv[SECRET_STORAGE_IV_SIZE]; /* IV associé */ + int ret; /* Bilan à d'un appel */ + EVP_CIPHER_CTX *ctx; /* Contexte pour le chiffrement*/ + size_t needed; /* Taille de la sortie */ + unsigned char *iter; /* Tête d'écriture */ + int outlen; /* Taille des données utiles */ + + result = false; + + if (g_secret_storage_is_locked(storage)) + goto quick_exit; + + /* Récupération de la clef maître et d'un IV de chiffrement */ + + ret = RAND_bytes(iv, sizeof(iv)); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit; + } + + /* Préparation de la zone de réception */ + + needed = SECRET_STORAGE_IV_SIZE + ((in->size / SECRET_STORAGE_BLOCK_SIZE) + 1) * SECRET_STORAGE_BLOCK_SIZE; + + setup_sized_binary(out, needed); + + /* Chiffrement des données */ + + ctx = EVP_CIPHER_CTX_new(); + + if (ctx == NULL) + { + LOG_ERROR_OPENSSL; + goto exit; + } + + ret = EVP_EncryptInit_ex2(ctx, EVP_aes_256_cbc(), storage->master_key, iv, NULL); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + memcpy(out->data, iv, SECRET_STORAGE_IV_SIZE); + + iter = out->bin_data + SECRET_STORAGE_IV_SIZE; + + ret = EVP_EncryptUpdate(ctx, iter, &outlen, in->bin_data, in->size); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter += outlen; + + ret = EVP_EncryptFinal_ex(ctx, iter, &outlen); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter += outlen; + + assert((iter - out->bin_data) == out->size); + + result = true; + + /* Sortie */ + + exit_with_ctx: + + EVP_CIPHER_CTX_free(ctx); + + if (!result) + exit_sized_binary(out); + + exit: + quick_exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à consulter. * +* in = séquence d'octets à traiter. * +* out = séquence d'octets résultantes. [OUT] * +* * +* Description : Déchiffre des données avec la clef de chiffrement maître. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_decrypt_data(const GSecretStorage *storage, const sized_binary_t *in, sized_binary_t *out) +{ + bool result; /* Bilan à retourner */ + unsigned char iv[SECRET_STORAGE_IV_SIZE]; /* IV associé */ + int ret; /* Bilan à d'un appel */ + EVP_CIPHER_CTX *ctx; /* Contexte pour le chiffrement*/ + size_t needed; /* Taille de la sortie */ + unsigned char *iter; /* Tête d'écriture */ + int outlen; /* Taille des données utiles */ + + result = false; + + if (g_secret_storage_is_locked(storage)) + goto quick_exit; + + /* Récupération d'un IV de déchiffrement */ + + if (in->size < SECRET_STORAGE_IV_SIZE) + goto exit; + + memcpy(iv, in->data, SECRET_STORAGE_IV_SIZE); + + /* Préparation de la zone de réception */ + + needed = in->size - SECRET_STORAGE_IV_SIZE; + + setup_sized_binary(out, needed); + + /* Chiffrement des données */ + + ctx = EVP_CIPHER_CTX_new(); + + if (ctx == NULL) + { + LOG_ERROR_OPENSSL; + goto exit; + } + + ret = EVP_DecryptInit_ex2(ctx, EVP_aes_256_cbc(), storage->master_key, iv, NULL); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter = out->bin_data; + + ret = EVP_DecryptUpdate(ctx, iter, &outlen, + in->bin_data + SECRET_STORAGE_IV_SIZE, in->size - SECRET_STORAGE_IV_SIZE); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter += outlen; + + ret = EVP_DecryptFinal_ex(ctx, iter, &outlen); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter += outlen; + + assert((iter - out->bin_data) <= out->size); + + resize_sized_binary(out, iter - out->bin_data); + + result = true; + + /* Sortie */ + + exit_with_ctx: + + EVP_CIPHER_CTX_free(ctx); + + if (!result) + exit_sized_binary(out); + + exit: + quick_exit: + + return result; + +} diff --git a/src/glibext/secstorage.h b/src/glibext/secstorage.h new file mode 100644 index 0000000..a75b1a3 --- /dev/null +++ b/src/glibext/secstorage.h @@ -0,0 +1,68 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * secstorage.h - prototypes pour la conservation sécurisée d'éléments de configuration + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see . + */ + + +#ifndef _GLIBEXT_SECSTORAGE_H +#define _GLIBEXT_SECSTORAGE_H + + +#include +#include + + +#include "helpers.h" +#include "../common/szbin.h" + + + +#define G_TYPE_SECRET_STORAGE (g_secret_storage_get_type()) + +DECLARE_GTYPE(GSecretStorage, g_secret_storage, G, SECRET_STORAGE); + + +/* Créé un nouveau gardien des secrets avec support de stockage. */ +GSecretStorage *g_secret_storage_new(GSettings *); + +/* Détermine si une clef de chiffrement protégée est en place. */ +bool g_secret_storage_has_key(const GSecretStorage *); + +/* Définit un mot de passe pour protéger une clef maître. */ +bool g_secret_storage_set_password(const GSecretStorage *, const char *); + +/* Détermine si la clef de chiffrement maître est vérouillée. */ +bool g_secret_storage_is_locked(const GSecretStorage *); + +/* Déverrouille la clef de chiffrement maître. */ +bool g_secret_storage_unlock(GSecretStorage *, const char *); + +/* Verrouille la clef de chiffrement maître. */ +void g_secret_storage_lock(GSecretStorage *); + +/* Chiffre des données avec la clef de chiffrement maître. */ +bool g_secret_storage_encrypt_data(const GSecretStorage *, const sized_binary_t *, sized_binary_t *); + +/* Déchiffre des données avec la clef de chiffrement maître. */ +bool g_secret_storage_decrypt_data(const GSecretStorage *, const sized_binary_t *, sized_binary_t *); + + + +#endif /* _GLIBEXT_SECSTORAGE_H */ diff --git a/tests/core/re.chrysalide.tests.secstorage.gschema.xml b/tests/core/re.chrysalide.tests.secstorage.gschema.xml deleted file mode 100644 index 6afa96b..0000000 --- a/tests/core/re.chrysalide.tests.secstorage.gschema.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - [] - - - - [] - - - - - diff --git a/tests/core/secstorage.py b/tests/core/secstorage.py deleted file mode 100644 index 1f82388..0000000 --- a/tests/core/secstorage.py +++ /dev/null @@ -1,150 +0,0 @@ - -import gi -import os -import subprocess - -from chrysacase import ChrysalideTestCase -from pychrysalide import core -from gi.repository import Gio, GLib - - -class TestSecretStorage(ChrysalideTestCase): - """TestCase for secret storage features.""" - - @classmethod - def setUpClass(cls): - - super(TestSecretStorage, cls).setUpClass() - - cls.log('Creating GSettings schema...') - - path = os.path.dirname(os.path.realpath(__file__)) - - subprocess.run([ 'glib-compile-schemas', path ]) - - os.environ['GSETTINGS_SCHEMA_DIR'] = path + ':' + os.environ['GSETTINGS_SCHEMA_DIR'] - - - @classmethod - def tearDownClass(cls): - - super(TestSecretStorage, cls).tearDownClass() - - cls.log('Removing compiled GSettings schema...') - - os.environ['GSETTINGS_SCHEMA_DIR'] = ':'.join(os.environ['GSETTINGS_SCHEMA_DIR'].split(':')[1:]) - - path = os.path.dirname(os.path.realpath(__file__)) - - filename = os.path.join(path, 'gschemas.compiled') - - if os.path.exists(filename): - os.remove(filename) - - - def testMasterKeyDefinition(self): - """Check for cryptographic parameters for secret storage.""" - - settings = Gio.Settings.new('re.chrysalide.tests.secstorage') - - settings.reset('master') - - self.assertEqual(len(settings.get_value('master').unpack()), 0) - - self.assertFalse(core.has_secret_storage_key(settings)) - - settings.set_value('master', GLib.Variant('ay', b'ABC')) - - self.assertFalse(core.has_secret_storage_key(settings)) - - settings.set_value('master', GLib.Variant('ay', b'A' * 23)) - - self.assertTrue(core.has_secret_storage_key(settings)) - - - def testMasterKeyCreation(self): - """Create and update cryptographic parameters for secret storage.""" - - settings = Gio.Settings.new('re.chrysalide.tests.secstorage') - - settings.reset('salt') - settings.reset('master') - - status = core.has_secret_storage_key(settings) - - self.assertFalse(status); - - status = core.set_secret_storage_password(settings, '') - - self.assertTrue(status); - - status = core.has_secret_storage_key(settings) - - self.assertTrue(status); - - status = core.is_secret_storage_locked(settings) - - self.assertTrue(status) - - status = core.unlock_secret_storage(settings, '') - - self.assertTrue(status) - - status = core.is_secret_storage_locked(settings) - - self.assertFalse(status) - - core.lock_secret_storage(settings) - - status = core.is_secret_storage_locked(settings) - - self.assertTrue(status) - - status = core.unlock_secret_storage(settings, 'XXX') - - self.assertFalse(status) - - status = core.is_secret_storage_locked(settings) - - self.assertTrue(status) - - - def testDataEncryption(self): - """Create and update cryptographic parameters for secret storage.""" - - settings = Gio.Settings.new('re.chrysalide.tests.secstorage') - - settings.reset('salt') - settings.reset('master') - - status = core.set_secret_storage_password(settings, '') - - self.assertTrue(status); - - status = core.unlock_secret_storage(settings, '') - - self.assertTrue(status) - - - original = b'ABC' - - encrypted = core.encrypt_secret_storage_data(original, settings) - - self.assertIsNotNone(encrypted) - - plain = core.decrypt_secret_storage_data(encrypted, settings) - - self.assertIsNotNone(plain) - self.assertEqual(original, plain) - - - original = b'A' * 136 - - encrypted = core.encrypt_secret_storage_data(original, settings) - - self.assertIsNotNone(encrypted) - - plain = core.decrypt_secret_storage_data(encrypted, settings) - - self.assertIsNotNone(plain) - self.assertEqual(original, plain) diff --git a/tests/glibext/re.chrysalide.tests.secstorage.gschema.xml b/tests/glibext/re.chrysalide.tests.secstorage.gschema.xml new file mode 100644 index 0000000..6afa96b --- /dev/null +++ b/tests/glibext/re.chrysalide.tests.secstorage.gschema.xml @@ -0,0 +1,15 @@ + + + + + + [] + + + + [] + + + + + diff --git a/tests/glibext/secstorage.py b/tests/glibext/secstorage.py new file mode 100644 index 0000000..248b3e3 --- /dev/null +++ b/tests/glibext/secstorage.py @@ -0,0 +1,143 @@ + +import gi +import os +import subprocess + +from chrysacase import ChrysalideTestCase +from gi.repository import Gio, GLib +from pychrysalide.glibext import SecretStorage + + +class TestSecretStorage(ChrysalideTestCase): + """TestCase for secret storage features.""" + + @classmethod + def setUpClass(cls): + + super(TestSecretStorage, cls).setUpClass() + + cls.log('Creating GSettings schema...') + + path = os.path.dirname(os.path.realpath(__file__)) + + subprocess.run([ 'glib-compile-schemas', path ]) + + os.environ['GSETTINGS_SCHEMA_DIR'] = path + ':' + os.environ['GSETTINGS_SCHEMA_DIR'] + + + @classmethod + def tearDownClass(cls): + + super(TestSecretStorage, cls).tearDownClass() + + cls.log('Removing compiled GSettings schema...') + + os.environ['GSETTINGS_SCHEMA_DIR'] = ':'.join(os.environ['GSETTINGS_SCHEMA_DIR'].split(':')[1:]) + + path = os.path.dirname(os.path.realpath(__file__)) + + filename = os.path.join(path, 'gschemas.compiled') + + if os.path.exists(filename): + os.remove(filename) + + + def testMasterKeyDefinition(self): + """Check for cryptographic parameters for secret storage.""" + + settings = Gio.Settings.new('re.chrysalide.tests.secstorage') + + storage = SecretStorage(settings) + + settings.reset('master') + + self.assertEqual(len(settings.get_value('master').unpack()), 0) + + self.assertFalse(storage.has_key) + + settings.set_value('master', GLib.Variant('ay', b'ABC')) + + self.assertFalse(storage.has_key) + + settings.set_value('master', GLib.Variant('ay', b'A' * 23)) + + self.assertTrue(storage.has_key) + + + def testMasterKeyCreation(self): + """Create and update cryptographic parameters for secret storage.""" + + settings = Gio.Settings.new('re.chrysalide.tests.secstorage') + + storage = SecretStorage(settings) + + settings.reset('salt') + settings.reset('master') + + self.assertFalse(storage.has_key) + + status = storage.set_password('') + + self.assertTrue(status); + + self.assertTrue(storage.has_key) + self.assertTrue(storage.is_locked) + + status = storage.unlock('') + + self.assertTrue(status) + + self.assertFalse(storage.is_locked) + + storage.lock() + + self.assertTrue(storage.is_locked) + + status = storage.unlock('XXX') + + self.assertFalse(status) + + self.assertTrue(storage.is_locked) + + + def testDataEncryption(self): + """Create and update cryptographic parameters for secret storage.""" + + settings = Gio.Settings.new('re.chrysalide.tests.secstorage') + + storage = SecretStorage(settings) + + settings.reset('salt') + settings.reset('master') + + status = storage.set_password('') + + self.assertTrue(status); + + status = storage.unlock('') + + self.assertTrue(status) + + + original = b'ABC' + + encrypted = storage.encrypt_data(original) + + self.assertIsNotNone(encrypted) + + plain = storage.decrypt_data(encrypted) + + self.assertIsNotNone(plain) + self.assertEqual(original, plain) + + + original = b'A' * 136 + + encrypted = storage.encrypt_data(original) + + self.assertIsNotNone(encrypted) + + plain = storage.decrypt_data(encrypted) + + self.assertIsNotNone(plain) + self.assertEqual(original, plain) -- cgit v0.11.2-87-g4458