/* 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 "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 *);

/* Teste un mot de passe par Déverrouillage de clef maître. */
static bool g_secret_storage_check_primary_password(GSecretStorage *, const char *, void **);



/* 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;

    g_signal_new("lock-update",
                 G_TYPE_SECRET_STORAGE,
                 G_SIGNAL_RUN_LAST,
                 G_STRUCT_OFFSET(GSecretStorageClass, lock_update),
                 NULL, NULL,
                 g_cclosure_marshal_VOID__VOID,
                 G_TYPE_NONE, 0);

}


/******************************************************************************
*                                                                             *
*  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
        storage->settings = g_settings_new("re.chrysalide.framework.secstorage");

    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(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);

    g_signal_emit_by_name(storage, "lock-update");

    result = true;

 exit_with_ctx:

    EVP_CIPHER_CTX_free(ctx);

 exit:

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : storage = espace de stockage sécurisé à consulter.           *
*                old     = ancien mot de passe principal à vérifier.          *
*                new     = nouveau mot de passe principal à appliquer.        *
*                                                                             *
*  Description : Modifie le mot de passe protégeant une clef maître.          *
*                                                                             *
*  Retour      : Bilan de la mise en place.                                   *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool g_secret_storage_change_password(GSecretStorage *storage, const char *old, const char *new)
{
    bool result;                            /* Bilan à retourner           */

    result = false;

    if (!g_secret_storage_has_key(storage))
        goto exit;

    if (!g_secret_storage_check_primary_password(storage, old, NULL))
        goto exit;

    if (!g_secret_storage_is_locked(storage))
        g_secret_storage_lock(storage);

    g_settings_reset(storage->settings, "salt");
    g_settings_reset(storage->settings, "master");

    result = g_secret_storage_set_password(storage, new);

 exit:

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : storage  = espace de stockage sécurisé à consulter.          *
*                password = mot de passe principal à appliquer.               *
*                                                                             *
*  Description : Supprime le mot de passe protégeant une clef maître.         *
*                                                                             *
*  Retour      : Bilan de la mise en place.                                   *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool g_secret_storage_remove_password(GSecretStorage *storage, const char *passwd)
{
    bool result;                            /* Bilan à retourner           */

    result = false;

    if (!g_secret_storage_has_key(storage))
        goto exit;

    if (!g_secret_storage_check_primary_password(storage, passwd, NULL))
        goto exit;

    if (!g_secret_storage_is_locked(storage))
        g_secret_storage_lock(storage);

    g_settings_reset(storage->settings, "salt");
    g_settings_reset(storage->settings, "master");

    g_signal_emit_by_name(storage, "lock-update");

    result = true;

 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.                *
*                master   = éventuelle adresse pour un stockage de clef. [OUT]*
*                                                                             *
*  Description : Teste un mot de passe par Déverrouillage de clef maître.     *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool g_secret_storage_check_primary_password(GSecretStorage *storage, const char *passwd, void **master)
{
    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 */

    if (master != NULL)
    {
        *master = malloc(SECRET_STORAGE_KEY_SIZE);
        memcpy(*master, 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.          *
*                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           */

    result = g_secret_storage_check_primary_password(storage, passwd, &storage->master_key);

    if (result)
        g_signal_emit_by_name(storage, "lock-update");

    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;

        g_signal_emit_by_name(storage, "lock-update");

    }

}


/******************************************************************************
*                                                                             *
*  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;

}