diff options
| author | Cyrille Bagard <nocbos@gmail.com> | 2025-02-08 15:57:23 (GMT) | 
|---|---|---|
| committer | Cyrille Bagard <nocbos@gmail.com> | 2025-02-08 15:57:23 (GMT) | 
| commit | 71d0b80eca2fd2aed5883e2a6a57cb8c03aa27ff (patch) | |
| tree | 74c9654c9c6d02059ba9aff4536ce0ea25e7763c | |
| parent | c928f8abb669d37e77bd9056240074941a945bb9 (diff) | |
Introduce a secure storage.
| -rw-r--r-- | plugins/pychrysalide/Makefile.am | 1 | ||||
| -rw-r--r-- | plugins/pychrysalide/convert.c | 89 | ||||
| -rw-r--r-- | plugins/pychrysalide/convert.h | 38 | ||||
| -rw-r--r-- | plugins/pychrysalide/core/Makefile.am | 3 | ||||
| -rw-r--r-- | plugins/pychrysalide/core/module.c | 2 | ||||
| -rw-r--r-- | plugins/pychrysalide/core/secstorage.c | 465 | ||||
| -rw-r--r-- | plugins/pychrysalide/core/secstorage.h | 39 | ||||
| -rw-r--r-- | src/common/szbin.h | 10 | ||||
| -rw-r--r-- | src/core/Makefile.am | 5 | ||||
| -rw-r--r-- | src/core/core.c | 5 | ||||
| -rw-r--r-- | src/core/secstorage.c | 779 | ||||
| -rw-r--r-- | src/core/secstorage.h | 65 | ||||
| -rw-r--r-- | tests/core/re.chrysalide.tests.secstorage.gschema.xml | 15 | ||||
| -rw-r--r-- | tests/core/secstorage.py | 150 | 
14 files changed, 1663 insertions, 3 deletions
| diff --git a/plugins/pychrysalide/Makefile.am b/plugins/pychrysalide/Makefile.am index 7b1a331..d1bf457 100644 --- a/plugins/pychrysalide/Makefile.am +++ b/plugins/pychrysalide/Makefile.am @@ -34,6 +34,7 @@ pychrysalide_la_SOURCES =					\  	access.h access.c						\  	bindings.h bindings.c					\  	constants.h constants.c					\ +	convert.h convert.c						\  	core-int.h								\  	core.h core.c							\  	helpers.h helpers.c						\ diff --git a/plugins/pychrysalide/convert.c b/plugins/pychrysalide/convert.c new file mode 100644 index 0000000..c67c8ba --- /dev/null +++ b/plugins/pychrysalide/convert.c @@ -0,0 +1,89 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * convert.c - conversion d'arguments en éléments usuels externes + * + * 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 "convert.h" + + +#include <assert.h> +#include <pygobject.h> +#ifndef NDEBUG +#   include <stdbool.h> +#endif +#include <gio/gio.h> + + + +/****************************************************************************** +*                                                                             * +*  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 instance GSettings.                    * +*                                                                             * +*  Retour      : Bilan de l'opération, voire indications supplémentaires.     * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +int convert_to_gsettings(PyObject *arg, void *dst) +{ +    int result;                             /* Bilan à retourner           */ +    GType type;                             /* Type obtenu ou 0            */ + +    result = PyObject_IsInstance(arg, (PyObject *)&PyGObject_Type); + +    if (result == 1) +    { +        type = pyg_type_from_object(arg); + +        if (type != G_TYPE_SETTINGS) +            result = 0; + +    } + +    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 GSetting instance"); +            break; + +        case 1: +            *((GSettings **)dst) = G_SETTINGS(pygobject_get(arg)); +            break; + +        default: +            assert(false); +            break; + +    } + +    return result; + +} diff --git a/plugins/pychrysalide/convert.h b/plugins/pychrysalide/convert.h new file mode 100644 index 0000000..7bdf7da --- /dev/null +++ b/plugins/pychrysalide/convert.h @@ -0,0 +1,38 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * convert.h - prototypes pour la conversion d'arguments en éléments usuels externes + * + * 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_CONVERT_H +#define _PLUGINS_PYCHRYSALIDE_CONVERT_H + + +#include <Python.h> + + + +/* Tente de convertir en instance GSettings. */ +int convert_to_gsettings(PyObject *, void *); + + + +#endif  /* _PLUGINS_PYCHRYSALIDE_CONVERT_H */ diff --git a/plugins/pychrysalide/core/Makefile.am b/plugins/pychrysalide/core/Makefile.am index 5588c9f..6ba9fc8 100644 --- a/plugins/pychrysalide/core/Makefile.am +++ b/plugins/pychrysalide/core/Makefile.am @@ -15,7 +15,8 @@ libpychrysacore_la_SOURCES =				\  	constants.h constants.c					\  	logs.h logs.c							\  	module.h module.c						\ -	nox.h nox.c +	nox.h nox.c								\ +	secstorage.h secstorage.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 4af0403..7eceddd 100644 --- a/plugins/pychrysalide/core/module.c +++ b/plugins/pychrysalide/core/module.c @@ -35,6 +35,7 @@  //#include "params.h"  //#include "processors.h"  //#include "queue.h" +#include "secstorage.h"  #include "../helpers.h" @@ -110,6 +111,7 @@ 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 new file mode 100644 index 0000000..67779af --- /dev/null +++ b/plugins/pychrysalide/core/secstorage.c @@ -0,0 +1,465 @@ + +/* 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 <core/secstorage.h> + + +#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 new file mode 100644 index 0000000..d05d052 --- /dev/null +++ b/plugins/pychrysalide/core/secstorage.h @@ -0,0 +1,39 @@ + +/* 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 <Python.h> +#include <stdbool.h> + + + +/* 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/src/common/szbin.h b/src/common/szbin.h index 5891449..23aac67 100644 --- a/src/common/szbin.h +++ b/src/common/szbin.h @@ -97,6 +97,16 @@ typedef struct _sized_binary_t      while (0) +#define resize_sized_binary(sb, s)          \ +    do                                      \ +    {                                       \ +        (sb)->size = s;                     \ +        (sb)->data = realloc((sb)->data,    \ +                             (sb)->size);   \ +    }                                       \ +    while (0) + +  #define add_to_sized_binary(sb, d, s)       \      do                                      \      {                                       \ diff --git a/src/core/Makefile.am b/src/core/Makefile.am index e1e3c4e..906c383 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -20,9 +20,10 @@ libcore4_la_SOURCES =						\  	logs.h logs.c							\  	nox.h nox.c								\  	nproc.h nproc.c							\ -	paths.h paths.c +	paths.h paths.c							\ +	secstorage.h secstorage.c -libcore4_la_CFLAGS = $(TOOLKIT_CFLAGS) +libcore4_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBSSL_CFLAGS)  devdir = $(includedir)/chrysalide/$(subdir:src/%=core/%) diff --git a/src/core/core.c b/src/core/core.c index 9514ee1..eaf7763 100644 --- a/src/core/core.c +++ b/src/core/core.c @@ -25,6 +25,7 @@  #include "global.h" +#include "secstorage.h" @@ -53,6 +54,8 @@ bool load_core_components(AvailableCoreComponent flags)      if ((flags & ACC_GLOBAL_VARS) != 0 && (__loaded & ACC_GLOBAL_VARS) == 0)      { +        init_secret_storage(); +          set_work_queue(g_work_queue_new());          __loaded |= ACC_GLOBAL_VARS; @@ -82,6 +85,8 @@ void unload_core_components(AvailableCoreComponent flags)      {          set_work_queue(NULL); +        exit_secret_storage(); +          __loaded &= ~ACC_GLOBAL_VARS;      } diff --git a/src/core/secstorage.c b/src/core/secstorage.c new file mode 100644 index 0000000..7f57b1c --- /dev/null +++ b/src/core/secstorage.c @@ -0,0 +1,779 @@ + +/* 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 <http://www.gnu.org/licenses/>. + */ + + +#include "secstorage.h" + + +#include <assert.h> +#include <string.h> +#include <openssl/evp.h> +#include <openssl/rand.h> + + +#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 new file mode 100644 index 0000000..7c27e07 --- /dev/null +++ b/src/core/secstorage.h @@ -0,0 +1,65 @@ + +/* 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _CORE_SECSTORAGE_H +#define _CORE_SECSTORAGE_H + + +#include <stdbool.h> +#include <gio/gio.h> + + +#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/tests/core/re.chrysalide.tests.secstorage.gschema.xml b/tests/core/re.chrysalide.tests.secstorage.gschema.xml new file mode 100644 index 0000000..6afa96b --- /dev/null +++ b/tests/core/re.chrysalide.tests.secstorage.gschema.xml @@ -0,0 +1,15 @@ +<schemalist> + +    <schema id="re.chrysalide.tests.secstorage" path="/re/chrysalide/tests/secstorage/"> + +        <key name="salt" type="ay"> +            <default>[]</default> +	    </key> + +        <key name="master" type="ay"> +            <default>[]</default> +	    </key> + +    </schema> + +</schemalist> diff --git a/tests/core/secstorage.py b/tests/core/secstorage.py new file mode 100644 index 0000000..1f82388 --- /dev/null +++ b/tests/core/secstorage.py @@ -0,0 +1,150 @@ + +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, '<s3cUre>') + +        self.assertTrue(status); + +        status = core.unlock_secret_storage(settings, '<s3cUre>') + +        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) | 
