/* Chrysalide - Outil d'analyse de fichiers binaires * secstorage.c - conservation sécurisée d'éléments de configuration * * Copyright (C) 2025 Cyrille Bagard * * This file is part of Chrysalide. * * Chrysalide is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * Chrysalide is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Chrysalide. If not, see . */ #include "secstorage.h" #include #include #include #include #include "secstorage-int.h" #include "../core/logs.h" /** * Les mécanismes de hachage de mot de passe doivent être utilisés avec un sel, * et la longueur du sel doit être d’au moins 128 bits. * * Cette note concerne le hachage de mots de passe et non la dérivation de secrets * cependant. * * Source : https://cyber.gouv.fr/sites/default/files/2021/03/anssi-guide-selection_crypto-1.0.pdf */ #define SECRET_STORAGE_SALT_SIZE (256 / 8) /** * Nombre d'itérations pour PBKDF2 : en 2023, OWASP recommande 600000 itérations * pour PBKDF2-HMAC-SHA256 (et 210000 pour PBKDF2-HMAC-SHA512). * * Source : https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2 */ #define PBKDF2_HMAC_SHA256_ITERATIONS (2 << 20) /** * AES 256 : clef de 256 bits, IV de 128 bits */ #define SECRET_STORAGE_KEK_SIZE (256 / 8) #define SECRET_STORAGE_KEY_SIZE (256 / 8) #define SECRET_STORAGE_BLOCK_SIZE (128 / 8) #define SECRET_STORAGE_IV_SIZE SECRET_STORAGE_BLOCK_SIZE /* Initialise la classe des stockages de secrets. */ static void g_secret_storage_class_init(GSecretStorageClass *); /* Initialise une instance de stockage de secrets. */ static void g_secret_storage_init(GSecretStorage *); /* Supprime toutes les références externes. */ static void g_secret_storage_dispose(GObject *); /* Procède à la libération totale de la mémoire. */ static void g_secret_storage_finalize(GObject *); /* Indique le type défini pour un gardien des secrets avec support des stockages. */ G_DEFINE_TYPE(GSecretStorage, g_secret_storage, G_TYPE_OBJECT); /****************************************************************************** * * * Paramètres : klass = classe à initialiser. * * * * Description : Initialise la classe des stockages de secrets. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_secret_storage_class_init(GSecretStorageClass *klass) { GObjectClass *object; /* Autre version de la classe */ object = G_OBJECT_CLASS(klass); object->dispose = g_secret_storage_dispose; object->finalize = g_secret_storage_finalize; } /****************************************************************************** * * * Paramètres : storage = instance à initialiser. * * * * Description : Initialise une instance de stockage de secrets. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_secret_storage_init(GSecretStorage *storage) { storage->settings = NULL; storage->master_key = NULL; } /****************************************************************************** * * * Paramètres : object = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_secret_storage_dispose(GObject *object) { GSecretStorage *storage; /* Gestion de stockage sécurisé*/ storage = G_SECRET_STORAGE(object); g_clear_object(&storage->settings); G_OBJECT_CLASS(g_secret_storage_parent_class)->dispose(object); } /****************************************************************************** * * * Paramètres : object = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_secret_storage_finalize(GObject *object) { GSecretStorage *storage; /* Gestion de stockage sécurisé*/ storage = G_SECRET_STORAGE(object); if (storage->master_key != NULL) free(storage->master_key); G_OBJECT_CLASS(g_secret_storage_parent_class)->finalize(object); } /****************************************************************************** * * * Paramètres : settings = éventuel espace de configuration à utiliser. * * * * Description : Créé un nouveau gardien des secrets avec support de stockage.* * * * Retour : Gestionnaire de stockage sécurisé mis en place. * * * * Remarques : - * * * ******************************************************************************/ GSecretStorage *g_secret_storage_new(GSettings *settings) { GSecretStorage *result; /* Instance à retourner */ result = g_object_new(G_TYPE_SECRET_STORAGE, NULL); if (!g_secret_storage_create(result, settings)) g_clear_object(&result); return result; } /****************************************************************************** * * * Paramètres : storage = stockage sécurisé à initialiser. * * settings = éventuel espace de configuration à utiliser. * * * * Description : Met en place un gardien des secrets avec support de stockage.* * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool g_secret_storage_create(GSecretStorage *storage, GSettings *settings) { bool result; /* Bilan à retourner */ result = true; if (settings != NULL) { ref_object(settings); storage->settings = settings; } else result = false; // TODO return result; } /****************************************************************************** * * * Paramètres : storage = espace de stockage sécurisé à consulter. * * * * Description : Détermine si une clef de chiffrement protégée est en place. * * * * Retour : Bilan de l'analyse. * * * * Remarques : - * * * ******************************************************************************/ bool g_secret_storage_has_key(const GSecretStorage *storage) { bool result; /* Bilan à retourner */ GVariant *value; /* Valeur de configuration */ gsize length; /* Taille d'une valeur donnée */ result = false; value = g_settings_get_value(storage->settings, "master"); g_variant_get_fixed_array(value, &length, 1); result = (length > SECRET_STORAGE_IV_SIZE); g_variant_unref(value); return result;; } /****************************************************************************** * * * Paramètres : storage = espace de stockage sécurisé à consulter. * * password = mot de passe principal à appliquer. * * * * Description : Définit un mot de passe pour protéger une clef maître. * * * * Retour : Bilan de la mise en place. * * * * Remarques : - * * * ******************************************************************************/ bool g_secret_storage_set_password(const GSecretStorage *storage, const char *passwd) { bool result; /* Bilan à retourner */ unsigned char salt[SECRET_STORAGE_SALT_SIZE]; /* Sel pour la dérivation*/ int ret; /* Bilan à d'un appel */ GVariant *value; /* Valeur de configuration */ unsigned char kek[SECRET_STORAGE_KEK_SIZE]; /* Clef de protection */ unsigned char key[SECRET_STORAGE_KEY_SIZE]; /* Clef maître */ unsigned char iv[SECRET_STORAGE_IV_SIZE]; /* IV associé */ EVP_CIPHER_CTX *ctx; /* Contexte pour le chiffrement*/ unsigned char encrypted[64]; /* Partie chiffrée à conserver */ unsigned char *iter; /* Tête d'écriture */ int outlen; /* Taille des données utiles */ result = false; if (g_secret_storage_has_key(storage)) goto exit; /* Création d'un sel pour la dérivation du mot de passe */ ret = RAND_bytes(salt, sizeof(salt)); if (ret != 1) { LOG_ERROR_OPENSSL; goto exit; } /** * La fonction g_variant_new_fixed_array() retourne un variant * avec un décompte de référence flottant. */ value = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, salt, SECRET_STORAGE_SALT_SIZE, sizeof(unsigned char)); /** * Comme le variant à une référence flottante, la fonction * g_settings_set_value() consomme cette référence. * * Il n'y a donc pas lieu d'appeler g_variant_unref(). */ g_settings_set_value(storage->settings, "salt", value); /* Dérivation du mot de passe */ ret = PKCS5_PBKDF2_HMAC(passwd, strlen(passwd), salt, SECRET_STORAGE_SALT_SIZE, PBKDF2_HMAC_SHA256_ITERATIONS, EVP_sha256(), SECRET_STORAGE_KEK_SIZE, kek); if (ret != 1) { LOG_ERROR_OPENSSL; goto exit; } /* Définition de la clef maître et de son IV de chiffrement */ ret = RAND_bytes(key, sizeof(key)); if (ret != 1) { LOG_ERROR_OPENSSL; goto exit; } ret = RAND_bytes(iv, sizeof(iv)); if (ret != 1) { LOG_ERROR_OPENSSL; goto exit; } /* Chiffrement de la clef maître */ ctx = EVP_CIPHER_CTX_new(); if (ctx == NULL) { LOG_ERROR_OPENSSL; goto exit; } EVP_CIPHER_CTX_set_flags(ctx, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); ret = EVP_EncryptInit_ex2(ctx, EVP_aes_256_wrap_pad(), kek, iv, NULL); if (ret != 1) { LOG_ERROR_OPENSSL; goto exit_with_ctx; } memcpy(encrypted, iv, SECRET_STORAGE_IV_SIZE); iter = encrypted + SECRET_STORAGE_IV_SIZE; ret = EVP_EncryptUpdate(ctx, iter, &outlen, key, SECRET_STORAGE_KEY_SIZE); if (ret != 1) { LOG_ERROR_OPENSSL; goto exit_with_ctx; } iter += outlen; ret = EVP_EncryptFinal_ex(ctx, iter, &outlen); if (ret != 1) { LOG_ERROR_OPENSSL; goto exit_with_ctx; } iter += outlen; assert((iter - encrypted) < 64); /* Conservation de la clef protégée */ value = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, encrypted, iter - encrypted, sizeof(unsigned char)); g_settings_set_value(storage->settings, "master", value); result = true; exit_with_ctx: EVP_CIPHER_CTX_free(ctx); exit: return result; } /****************************************************************************** * * * Paramètres : storage = espace de stockage sécurisé à consulter. * * * * Description : Détermine si la clef de chiffrement maître est vérouillée. * * * * Retour : Bilan de la détermination. * * * * Remarques : - * * * ******************************************************************************/ bool g_secret_storage_is_locked(const GSecretStorage *storage) { bool result; /* Bilan à retourner */ result = (storage->master_key == NULL); return result; } /****************************************************************************** * * * Paramètres : storage = espace de stockage sécurisé à manipuler. * * password = mot de passe principal à utiliser. * * * * Description : Déverrouille la clef de chiffrement maître. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool g_secret_storage_unlock(GSecretStorage *storage, const char *passwd) { bool result; /* Bilan à retourner */ GVariant *salt_value; /* Valeur du sel configuré */ gsize salt_length; /* Taille du sel conservé */ gconstpointer salt; /* Données associées #1 */ unsigned char kek[SECRET_STORAGE_KEK_SIZE]; /* Clef de protection */ int ret; /* Bilan à d'un appel */ GVariant *enc_value; /* Paramètres de chiffrement */ gsize enc_length; /* Taille de ces paramètrs */ gconstpointer encrypted; /* Données associées #2 */ EVP_CIPHER_CTX *ctx; /* Contexte de déchiffrement */ unsigned char iv[SECRET_STORAGE_IV_SIZE]; /* IV associé */ unsigned char key[SECRET_STORAGE_KEY_SIZE]; /* Clef maître */ unsigned char *iter; /* Tête d'écriture */ int outlen; /* Taille des données utiles */ result = false; if (!g_secret_storage_is_locked(storage)) { result = true; goto quick_exit; } /* Récupération du sel mis en place */ salt_value = g_settings_get_value(storage->settings, "salt"); salt = g_variant_get_fixed_array(salt_value, &salt_length, sizeof(bin_t)); if (salt_length != SECRET_STORAGE_SALT_SIZE) goto exit_with_salt; /* Dérivation du mot de passe */ ret = PKCS5_PBKDF2_HMAC(passwd, strlen(passwd), salt, SECRET_STORAGE_SALT_SIZE, PBKDF2_HMAC_SHA256_ITERATIONS, EVP_sha256(), SECRET_STORAGE_KEK_SIZE, kek); if (ret != 1) { LOG_ERROR_OPENSSL; goto exit_with_salt; } /* Récupération des paramètres chiffrés */ enc_value = g_settings_get_value(storage->settings, "master"); encrypted = g_variant_get_fixed_array(enc_value, &enc_length, sizeof(bin_t)); if (enc_length <= SECRET_STORAGE_IV_SIZE) goto exit_with_enc; /* Déhiffrement de la clef maître */ ctx = EVP_CIPHER_CTX_new(); if (ctx == NULL) { LOG_ERROR_OPENSSL; goto exit_with_enc; } EVP_CIPHER_CTX_set_flags(ctx, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); memcpy(iv, encrypted, SECRET_STORAGE_IV_SIZE); ret = EVP_DecryptInit_ex2(ctx, EVP_aes_256_wrap_pad(), kek, iv, NULL); if (ret != 1) { LOG_ERROR_OPENSSL; goto exit_with_ctx; } iter = key; ret = EVP_DecryptUpdate(ctx, iter, &outlen, ((unsigned char *)encrypted) + SECRET_STORAGE_IV_SIZE, enc_length - SECRET_STORAGE_IV_SIZE); if (ret != 1) { LOG_ERROR_OPENSSL; goto exit_with_ctx; } iter += outlen; ret = EVP_DecryptFinal_ex(ctx, iter, &outlen); if (ret != 1) { LOG_ERROR_OPENSSL; goto exit_with_ctx; } assert((iter - key) == SECRET_STORAGE_KEY_SIZE); /* Stockage de la clef maître en mémoire */ storage->master_key = malloc(SECRET_STORAGE_KEY_SIZE); memcpy(storage->master_key, key, SECRET_STORAGE_KEY_SIZE); result = true; /* Sortie */ exit_with_ctx: EVP_CIPHER_CTX_free(ctx); exit_with_enc: g_variant_unref(enc_value); exit_with_salt: g_variant_unref(salt_value); quick_exit: return result; } /****************************************************************************** * * * Paramètres : storage = espace de stockage sécurisé à manipuler. * * * * Description : Verrouille la clef de chiffrement maître. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_secret_storage_lock(GSecretStorage *storage) { if (storage->master_key != NULL) { free(storage->master_key); storage->master_key = NULL; } } /****************************************************************************** * * * Paramètres : storage = espace de stockage sécurisé à consulter. * * in = séquence d'octets à traiter. * * out = séquence d'octets résultantes. [OUT] * * * * Description : Chiffre des données avec la clef de chiffrement maître. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool g_secret_storage_encrypt_data(const GSecretStorage *storage, const sized_binary_t *in, sized_binary_t *out) { bool result; /* Bilan à retourner */ unsigned char iv[SECRET_STORAGE_IV_SIZE]; /* IV associé */ int ret; /* Bilan à d'un appel */ EVP_CIPHER_CTX *ctx; /* Contexte pour le chiffrement*/ size_t needed; /* Taille de la sortie */ unsigned char *iter; /* Tête d'écriture */ int outlen; /* Taille des données utiles */ result = false; if (g_secret_storage_is_locked(storage)) goto quick_exit; /* Récupération de la clef maître et d'un IV de chiffrement */ ret = RAND_bytes(iv, sizeof(iv)); if (ret != 1) { LOG_ERROR_OPENSSL; goto exit; } /* Préparation de la zone de réception */ needed = SECRET_STORAGE_IV_SIZE + ((in->size / SECRET_STORAGE_BLOCK_SIZE) + 1) * SECRET_STORAGE_BLOCK_SIZE; setup_sized_binary(out, needed); /* Chiffrement des données */ ctx = EVP_CIPHER_CTX_new(); if (ctx == NULL) { LOG_ERROR_OPENSSL; goto exit; } ret = EVP_EncryptInit_ex2(ctx, EVP_aes_256_cbc(), storage->master_key, iv, NULL); if (ret != 1) { LOG_ERROR_OPENSSL; goto exit_with_ctx; } memcpy(out->data, iv, SECRET_STORAGE_IV_SIZE); iter = out->bin_data + SECRET_STORAGE_IV_SIZE; ret = EVP_EncryptUpdate(ctx, iter, &outlen, in->bin_data, in->size); if (ret != 1) { LOG_ERROR_OPENSSL; goto exit_with_ctx; } iter += outlen; ret = EVP_EncryptFinal_ex(ctx, iter, &outlen); if (ret != 1) { LOG_ERROR_OPENSSL; goto exit_with_ctx; } iter += outlen; assert((iter - out->bin_data) == out->size); result = true; /* Sortie */ exit_with_ctx: EVP_CIPHER_CTX_free(ctx); if (!result) exit_sized_binary(out); exit: quick_exit: return result; } /****************************************************************************** * * * Paramètres : storage = espace de stockage sécurisé à consulter. * * in = séquence d'octets à traiter. * * out = séquence d'octets résultantes. [OUT] * * * * Description : Déchiffre des données avec la clef de chiffrement maître. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool g_secret_storage_decrypt_data(const GSecretStorage *storage, const sized_binary_t *in, sized_binary_t *out) { bool result; /* Bilan à retourner */ unsigned char iv[SECRET_STORAGE_IV_SIZE]; /* IV associé */ int ret; /* Bilan à d'un appel */ EVP_CIPHER_CTX *ctx; /* Contexte pour le chiffrement*/ size_t needed; /* Taille de la sortie */ unsigned char *iter; /* Tête d'écriture */ int outlen; /* Taille des données utiles */ result = false; if (g_secret_storage_is_locked(storage)) goto quick_exit; /* Récupération d'un IV de déchiffrement */ if (in->size < SECRET_STORAGE_IV_SIZE) goto exit; memcpy(iv, in->data, SECRET_STORAGE_IV_SIZE); /* Préparation de la zone de réception */ needed = in->size - SECRET_STORAGE_IV_SIZE; setup_sized_binary(out, needed); /* Chiffrement des données */ ctx = EVP_CIPHER_CTX_new(); if (ctx == NULL) { LOG_ERROR_OPENSSL; goto exit; } ret = EVP_DecryptInit_ex2(ctx, EVP_aes_256_cbc(), storage->master_key, iv, NULL); if (ret != 1) { LOG_ERROR_OPENSSL; goto exit_with_ctx; } iter = out->bin_data; ret = EVP_DecryptUpdate(ctx, iter, &outlen, in->bin_data + SECRET_STORAGE_IV_SIZE, in->size - SECRET_STORAGE_IV_SIZE); if (ret != 1) { LOG_ERROR_OPENSSL; goto exit_with_ctx; } iter += outlen; ret = EVP_DecryptFinal_ex(ctx, iter, &outlen); if (ret != 1) { LOG_ERROR_OPENSSL; goto exit_with_ctx; } iter += outlen; assert((iter - out->bin_data) <= out->size); resize_sized_binary(out, iter - out->bin_data); result = true; /* Sortie */ exit_with_ctx: EVP_CIPHER_CTX_free(ctx); if (!result) exit_sized_binary(out); exit: quick_exit: return result; }