feat(osint): verify execution output integrity
This commit is contained in:
parent
bb8295af85
commit
9a4ba1de1c
8 changed files with 200 additions and 29 deletions
13
Makefile
13
Makefile
|
|
@ -119,6 +119,7 @@ TEST_OSINT_DNS_QUERY := tests/test_osint_dns_query
|
|||
TEST_OSINT_DNS_PROPOSAL := tests/test_osint_dns_proposal
|
||||
TEST_OSINT_DNS_INTEGRATION := tests/test_osint_dns_integration
|
||||
TEST_OSINT_EXECUTION_DAO := tests/test_osint_execution_dao
|
||||
TEST_OSINT_EXECUTION_INTEGRITY := tests/test_osint_execution_integrity
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
|
|
@ -589,6 +590,11 @@ $(TEST_OSINT_EXECUTION_DAO): \
|
|||
src/database/error.c
|
||||
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS) -lsqlite3
|
||||
|
||||
$(TEST_OSINT_EXECUTION_INTEGRITY): \
|
||||
tests/test_osint_execution_integrity.c \
|
||||
src/core/osint_execution_integrity.c
|
||||
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS)
|
||||
|
||||
$(TEST_INVESTIGATION_GRAPH_LOAD_TASK): \
|
||||
tests/test_investigation_graph_load_task.c \
|
||||
src/core/investigation_graph_load_task.c \
|
||||
|
|
@ -662,7 +668,8 @@ test: \
|
|||
$(TEST_OSINT_DNS_QUERY) \
|
||||
$(TEST_OSINT_DNS_PROPOSAL) \
|
||||
$(TEST_OSINT_DNS_INTEGRATION) \
|
||||
$(TEST_OSINT_EXECUTION_DAO)
|
||||
$(TEST_OSINT_EXECUTION_DAO) \
|
||||
$(TEST_OSINT_EXECUTION_INTEGRITY)
|
||||
@echo "Exécution des tests..."
|
||||
@./$(TEST_NODE)
|
||||
@./$(TEST_TREE_MODEL)
|
||||
|
|
@ -716,6 +723,7 @@ test: \
|
|||
@$(TEST_OSINT_DNS_PROPOSAL)
|
||||
@$(TEST_OSINT_DNS_INTEGRATION)
|
||||
@$(TEST_OSINT_EXECUTION_DAO)
|
||||
@$(TEST_OSINT_EXECUTION_INTEGRITY)
|
||||
@echo "Tous les tests sont valides."
|
||||
|
||||
%.o: %.c
|
||||
|
|
@ -775,7 +783,8 @@ clean:
|
|||
$(TEST_OSINT_DNS_QUERY) \
|
||||
$(TEST_OSINT_DNS_PROPOSAL) \
|
||||
$(TEST_OSINT_DNS_INTEGRATION) \
|
||||
$(TEST_OSINT_EXECUTION_DAO)
|
||||
$(TEST_OSINT_EXECUTION_DAO) \
|
||||
$(TEST_OSINT_EXECUTION_INTEGRITY)
|
||||
|
||||
-include $(DEP)
|
||||
|
||||
|
|
|
|||
|
|
@ -155,7 +155,9 @@ Le socle actuel comprend notamment :
|
|||
- provenance OSINT SQLite V3 conservant les arguments, sorties brutes,
|
||||
empreinte SHA-256 et liaisons vers les entités et relations intégrées ;
|
||||
- historique OSINT contextuel en lecture seule avec détail des exécutions,
|
||||
sorties standard et d'erreur, et objets créés ou réutilisés.
|
||||
sorties standard et d'erreur, et objets créés ou réutilisés ;
|
||||
- vérification manuelle de l'intégrité des sorties OSINT enregistrées, sans
|
||||
réécriture de l'empreinte ou des données contrôlées.
|
||||
|
||||
Les outils actuellement présents dans le catalogue initial sont :
|
||||
|
||||
|
|
|
|||
|
|
@ -1130,6 +1130,12 @@ l'entité ou la relation sélectionnée. La vue est strictement en lecture seule
|
|||
et expose les métadonnées, les sorties brutes rendues en UTF-8, l'empreinte et
|
||||
les objets liés avec leur disposition `created` ou `reused`.
|
||||
|
||||
Depuis ce détail, une vérification explicite recalcule l'empreinte SHA-256 à
|
||||
partir des BLOB `stdout_raw` et `stderr_raw`, séparés par l'octet nul défini
|
||||
lors de l'enregistrement. Le résultat indique si les sorties sont intactes,
|
||||
altérées ou impossibles à vérifier. Ce contrôle est strictement en lecture
|
||||
seule : aucune sortie ni empreinte persistée n'est corrigée automatiquement.
|
||||
|
||||
---
|
||||
|
||||
# 6. Tables de liaison
|
||||
|
|
|
|||
41
include/core/osint_execution_integrity.h
Normal file
41
include/core/osint_execution_integrity.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/******************************************************************************
|
||||
* @file osint_execution_integrity.h
|
||||
* @brief Calcul et vérification de l'intégrité des sorties OSINT.
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef LABFY_INVESTIGATION_OSINT_EXECUTION_INTEGRITY_H
|
||||
#define LABFY_INVESTIGATION_OSINT_EXECUTION_INTEGRITY_H
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/** @brief Résultat d'une vérification d'intégrité OSINT. */
|
||||
typedef enum
|
||||
{
|
||||
OSINT_EXECUTION_INTEGRITY_UNAVAILABLE = 0,
|
||||
OSINT_EXECUTION_INTEGRITY_INTACT,
|
||||
OSINT_EXECUTION_INTEGRITY_ALTERED
|
||||
} OsintExecutionIntegrityStatus;
|
||||
|
||||
/**
|
||||
* @brief Calcule le SHA-256 de stdout, d'un séparateur nul et de stderr.
|
||||
* @param stdout_raw Sortie standard brute.
|
||||
* @param stderr_raw Sortie d'erreur brute.
|
||||
* @return Empreinte hexadécimale possédée, ou NULL en cas d'échec.
|
||||
*/
|
||||
char *osint_execution_integrity_calculate(GBytes *stdout_raw, GBytes *stderr_raw);
|
||||
|
||||
/**
|
||||
* @brief Compare les sorties brutes à une empreinte SHA-256 enregistrée.
|
||||
* @param stdout_raw Sortie standard brute.
|
||||
* @param stderr_raw Sortie d'erreur brute.
|
||||
* @param expected_sha256 Empreinte attendue.
|
||||
* @return État d'intégrité sans modifier les données fournies.
|
||||
*/
|
||||
OsintExecutionIntegrityStatus osint_execution_integrity_verify(
|
||||
GBytes *stdout_raw, GBytes *stderr_raw, const char *expected_sha256
|
||||
);
|
||||
|
||||
G_END_DECLS
|
||||
#endif
|
||||
|
|
@ -26,6 +26,7 @@
|
|||
#include "core/tool_initializer.h"
|
||||
#include "core/tool_task.h"
|
||||
#include "core/osint_dns_integration.h"
|
||||
#include "core/osint_execution_integrity.h"
|
||||
#include "views/file_dialog.h"
|
||||
#include "core/evidence_import_task.h"
|
||||
#include "models/evidence_record.h"
|
||||
|
|
@ -1103,29 +1104,6 @@ static char *application_osint_create_timestamp(void)
|
|||
return timestamp;
|
||||
}
|
||||
|
||||
/** @brief Calcule l'empreinte déterministe de stdout et stderr bruts. */
|
||||
static char *application_osint_hash_outputs(GBytes *stdout_raw, GBytes *stderr_raw)
|
||||
{
|
||||
GChecksum *checksum = g_checksum_new(G_CHECKSUM_SHA256);
|
||||
gconstpointer data = NULL;
|
||||
gsize data_size = 0U;
|
||||
const guint8 separator = 0U;
|
||||
char *digest = NULL;
|
||||
if (checksum == NULL || stdout_raw == NULL || stderr_raw == NULL)
|
||||
{
|
||||
g_clear_pointer(&checksum, g_checksum_free);
|
||||
return NULL;
|
||||
}
|
||||
data = g_bytes_get_data(stdout_raw, &data_size);
|
||||
if (data_size > 0U) g_checksum_update(checksum, data, data_size);
|
||||
g_checksum_update(checksum, &separator, 1U);
|
||||
data = g_bytes_get_data(stderr_raw, &data_size);
|
||||
if (data_size > 0U) g_checksum_update(checksum, data, data_size);
|
||||
digest = g_strdup(g_checksum_get_string(checksum));
|
||||
g_checksum_free(checksum);
|
||||
return digest;
|
||||
}
|
||||
|
||||
/** @brief Persiste une exécution DNS terminée et retourne son UUID. */
|
||||
static char *application_persist_dns_execution(
|
||||
ApplicationOsintActionContext *context,
|
||||
|
|
@ -1157,7 +1135,7 @@ static char *application_persist_dns_execution(
|
|||
arguments = g_strdup_printf(
|
||||
"[\"+noall\",\"+answer\",\"%s\"]", context->target_value
|
||||
);
|
||||
sha256 = application_osint_hash_outputs(stdout_raw, stderr_raw);
|
||||
sha256 = osint_execution_integrity_calculate(stdout_raw, stderr_raw);
|
||||
record = osint_execution_record_new(
|
||||
identifier, "dns.dig",
|
||||
tool_info != NULL ? tool_info_get_detected_version(tool_info) : NULL,
|
||||
|
|
|
|||
44
src/core/osint_execution_integrity.c
Normal file
44
src/core/osint_execution_integrity.c
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/******************************************************************************
|
||||
* @file osint_execution_integrity.c
|
||||
* @brief Calcul et vérification de l'intégrité des sorties OSINT.
|
||||
******************************************************************************/
|
||||
|
||||
#include "core/osint_execution_integrity.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
char *osint_execution_integrity_calculate(GBytes *stdout_raw, GBytes *stderr_raw)
|
||||
{
|
||||
GChecksum *checksum = NULL;
|
||||
gconstpointer data = NULL;
|
||||
gsize data_size = 0U;
|
||||
const guint8 separator = 0U;
|
||||
char *digest = NULL;
|
||||
if (stdout_raw == NULL || stderr_raw == NULL) return NULL;
|
||||
checksum = g_checksum_new(G_CHECKSUM_SHA256);
|
||||
if (checksum == NULL) return NULL;
|
||||
data = g_bytes_get_data(stdout_raw, &data_size);
|
||||
if (data_size > 0U) g_checksum_update(checksum, data, data_size);
|
||||
g_checksum_update(checksum, &separator, 1U);
|
||||
data = g_bytes_get_data(stderr_raw, &data_size);
|
||||
if (data_size > 0U) g_checksum_update(checksum, data, data_size);
|
||||
digest = g_strdup(g_checksum_get_string(checksum));
|
||||
g_checksum_free(checksum);
|
||||
return digest;
|
||||
}
|
||||
|
||||
OsintExecutionIntegrityStatus osint_execution_integrity_verify(
|
||||
GBytes *stdout_raw, GBytes *stderr_raw, const char *expected_sha256
|
||||
)
|
||||
{
|
||||
char *calculated_sha256 = NULL;
|
||||
OsintExecutionIntegrityStatus status = OSINT_EXECUTION_INTEGRITY_UNAVAILABLE;
|
||||
if (expected_sha256 == NULL || strlen(expected_sha256) != 64U) return status;
|
||||
calculated_sha256 = osint_execution_integrity_calculate(stdout_raw, stderr_raw);
|
||||
if (calculated_sha256 != NULL)
|
||||
status = g_ascii_strcasecmp(calculated_sha256, expected_sha256) == 0
|
||||
? OSINT_EXECUTION_INTEGRITY_INTACT
|
||||
: OSINT_EXECUTION_INTEGRITY_ALTERED;
|
||||
g_free(calculated_sha256);
|
||||
return status;
|
||||
}
|
||||
|
|
@ -5,12 +5,15 @@
|
|||
|
||||
#include "views/osint_execution_history_dialog.h"
|
||||
|
||||
#include "core/osint_execution_integrity.h"
|
||||
#include "models/osint_execution_record.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GtkWindow *window;
|
||||
GtkTextBuffer *details_buffer;
|
||||
GtkWidget *integrity_label;
|
||||
const OsintExecutionRecord *current_record;
|
||||
GPtrArray *records;
|
||||
GHashTable *linked_objects;
|
||||
} OsintExecutionHistoryDialogContext;
|
||||
|
|
@ -96,6 +99,10 @@ static void osint_execution_history_dialog_on_record_clicked(
|
|||
if (context == NULL || encoded_index == 0U ||
|
||||
encoded_index > context->records->len) return;
|
||||
record = g_ptr_array_index(context->records, encoded_index - 1U);
|
||||
context->current_record = record;
|
||||
gtk_label_set_text(GTK_LABEL(context->integrity_label),
|
||||
"Intégrité : non vérifiée");
|
||||
gtk_widget_remove_css_class(context->integrity_label, "error");
|
||||
linked_objects = g_hash_table_lookup(context->linked_objects,
|
||||
osint_execution_record_get_identifier(record));
|
||||
details = osint_execution_history_dialog_build_details(record, linked_objects);
|
||||
|
|
@ -103,6 +110,33 @@ static void osint_execution_history_dialog_on_record_clicked(
|
|||
g_free(details);
|
||||
}
|
||||
|
||||
/** @brief Recalcule et affiche l'intégrité de l'exécution sélectionnée. */
|
||||
static void osint_execution_history_dialog_on_verify_clicked(
|
||||
GtkButton *button, gpointer user_data
|
||||
)
|
||||
{
|
||||
OsintExecutionHistoryDialogContext *context = user_data;
|
||||
GBytes *stdout_raw = NULL; GBytes *stderr_raw = NULL;
|
||||
OsintExecutionIntegrityStatus status;
|
||||
const char *status_text = "Intégrité : vérification impossible";
|
||||
(void) button;
|
||||
if (context == NULL || context->current_record == NULL) return;
|
||||
stdout_raw = osint_execution_record_ref_stdout(context->current_record);
|
||||
stderr_raw = osint_execution_record_ref_stderr(context->current_record);
|
||||
status = osint_execution_integrity_verify(
|
||||
stdout_raw, stderr_raw,
|
||||
osint_execution_record_get_output_sha256(context->current_record));
|
||||
if (status == OSINT_EXECUTION_INTEGRITY_INTACT)
|
||||
status_text = "Intégrité : intacte";
|
||||
else if (status == OSINT_EXECUTION_INTEGRITY_ALTERED)
|
||||
status_text = "Intégrité : altérée";
|
||||
gtk_label_set_text(GTK_LABEL(context->integrity_label), status_text);
|
||||
if (status == OSINT_EXECUTION_INTEGRITY_INTACT)
|
||||
gtk_widget_remove_css_class(context->integrity_label, "error");
|
||||
else gtk_widget_add_css_class(context->integrity_label, "error");
|
||||
g_bytes_unref(stdout_raw); g_bytes_unref(stderr_raw);
|
||||
}
|
||||
|
||||
void osint_execution_history_dialog_present(
|
||||
GtkWindow *parent_window, GPtrArray *records, GHashTable *linked_objects
|
||||
)
|
||||
|
|
@ -111,6 +145,7 @@ void osint_execution_history_dialog_present(
|
|||
GtkWidget *main_box = NULL; GtkWidget *content_box = NULL;
|
||||
GtkWidget *history_scroll = NULL; GtkWidget *history_box = NULL;
|
||||
GtkWidget *details_scroll = NULL; GtkWidget *details_view = NULL;
|
||||
GtkWidget *button_box = NULL; GtkWidget *verify_button = NULL;
|
||||
GtkWidget *close_button = NULL;
|
||||
if (records == NULL || records->len == 0U || linked_objects == NULL) return;
|
||||
context = g_new0(OsintExecutionHistoryDialogContext, 1);
|
||||
|
|
@ -160,12 +195,22 @@ void osint_execution_history_dialog_present(
|
|||
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(details_scroll), details_view);
|
||||
gtk_box_append(GTK_BOX(content_box), history_scroll);
|
||||
gtk_box_append(GTK_BOX(content_box), details_scroll);
|
||||
button_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
|
||||
gtk_widget_set_halign(button_box, GTK_ALIGN_END);
|
||||
context->integrity_label = gtk_label_new("Intégrité : non vérifiée");
|
||||
gtk_widget_set_hexpand(context->integrity_label, TRUE);
|
||||
gtk_widget_set_halign(context->integrity_label, GTK_ALIGN_START);
|
||||
verify_button = gtk_button_new_with_label("Vérifier l'intégrité");
|
||||
g_signal_connect(verify_button, "clicked",
|
||||
G_CALLBACK(osint_execution_history_dialog_on_verify_clicked), context);
|
||||
close_button = gtk_button_new_with_label("Fermer");
|
||||
gtk_widget_set_halign(close_button, GTK_ALIGN_END);
|
||||
g_signal_connect_swapped(close_button, "clicked",
|
||||
G_CALLBACK(gtk_window_destroy), context->window);
|
||||
gtk_box_append(GTK_BOX(button_box), verify_button);
|
||||
gtk_box_append(GTK_BOX(button_box), close_button);
|
||||
gtk_box_append(GTK_BOX(main_box), content_box);
|
||||
gtk_box_append(GTK_BOX(main_box), close_button);
|
||||
gtk_box_append(GTK_BOX(main_box), context->integrity_label);
|
||||
gtk_box_append(GTK_BOX(main_box), button_box);
|
||||
gtk_window_set_child(context->window, main_box);
|
||||
osint_execution_history_dialog_on_record_clicked(
|
||||
GTK_BUTTON(gtk_widget_get_first_child(history_box)), context);
|
||||
|
|
|
|||
46
tests/test_osint_execution_integrity.c
Normal file
46
tests/test_osint_execution_integrity.c
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/******************************************************************************
|
||||
* @file test_osint_execution_integrity.c
|
||||
* @brief Tests du contrôle d'intégrité des sorties OSINT.
|
||||
******************************************************************************/
|
||||
|
||||
#include "core/osint_execution_integrity.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
static void test_empty_outputs_are_intact(void)
|
||||
{
|
||||
GBytes *empty = g_bytes_new_static("", 0U);
|
||||
const char *expected =
|
||||
"6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d";
|
||||
g_assert_cmpint(osint_execution_integrity_verify(empty, empty, expected), ==,
|
||||
OSINT_EXECUTION_INTEGRITY_INTACT);
|
||||
g_bytes_unref(empty);
|
||||
}
|
||||
|
||||
static void test_binary_outputs_and_alteration(void)
|
||||
{
|
||||
const guint8 stdout_data[] = {0x41U, 0x00U, 0xFFU};
|
||||
const guint8 stderr_data[] = {0x80U, 0x42U};
|
||||
GBytes *stdout_raw = g_bytes_new_static(stdout_data, sizeof(stdout_data));
|
||||
GBytes *stderr_raw = g_bytes_new_static(stderr_data, sizeof(stderr_data));
|
||||
char *sha256 = osint_execution_integrity_calculate(stdout_raw, stderr_raw);
|
||||
g_assert_nonnull(sha256);
|
||||
g_assert_cmpint(osint_execution_integrity_verify(
|
||||
stdout_raw, stderr_raw, sha256), ==, OSINT_EXECUTION_INTEGRITY_INTACT);
|
||||
sha256[0] = sha256[0] == '0' ? '1' : '0';
|
||||
g_assert_cmpint(osint_execution_integrity_verify(
|
||||
stdout_raw, stderr_raw, sha256), ==, OSINT_EXECUTION_INTEGRITY_ALTERED);
|
||||
g_assert_cmpint(osint_execution_integrity_verify(
|
||||
NULL, stderr_raw, sha256), ==, OSINT_EXECUTION_INTEGRITY_UNAVAILABLE);
|
||||
g_free(sha256); g_bytes_unref(stdout_raw); g_bytes_unref(stderr_raw);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
g_test_add_func("/osint-execution-integrity/empty",
|
||||
test_empty_outputs_are_intact);
|
||||
g_test_add_func("/osint-execution-integrity/binary-altered",
|
||||
test_binary_outputs_and_alteration);
|
||||
return g_test_run();
|
||||
}
|
||||
Loading…
Reference in a new issue