• 8 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 03/01/2024

Utilisez la bibliothèque Libsodium pour vos applications

Dans ce chapitre, vous allez utiliser la bibliothèque cryptographique Libsodium pour développer une application sécurisée. Vous pourrez ainsi voir comment les algorithmes de cryptographie fonctionnent concrètement, et connaître les bonnes pratiques et pièges liés au développement de programmes cryptographiques.
Libsodium est une bibliothèque cryptographique simple et populaire développée en C, et disponible dans la plupart des langages de programmation. Dans ce chapitre, nous allons l'utiliser en PHP et en C.

Utilisez Libsodium avec PHP

Sous Windows

Vous pouvez télécharger PHP ici  (par exemple la version PHP 7.3 (7.3.2) VC15 x64 Non Thread Safe), puis extraire le contenu du dossier dans C:\php.

Il faut ensuite activer l'extension Libsodium de PHP. Dans le dossier C:\php, copiez le fichier  php.ini-development  et renommez-le  php.ini . Ouvrez le fichier  php.ini  avec un éditeur de texte, cherchez la ligne ;extension=sodium et décommentez-la en enlevant le point-virgule en début de ligne.

La commande pour exécuter un fichier PHP sous Windows est :

C:\php\php.exe -f [Chemin/nomFichier.php]

Sous Linux

Vous pouvez installer PHP avec la commande :

sudo apt-get install php

Il faut ensuite installer l'extension Libsodium de PHP avec la commande :

sudo pecl install -f libsodium

Enfin, pour activer Libsodium, ouvrez le fichier php.ini et ajoutez la ligne :

extension = sodium.so

La commande pour exécuter un fichier PHP sous Linux est :

php -f  [Chemin/nomFichier.php]

Hello World

La première application est très simple. Elle consiste à chiffrer le message "Hello World" avec une clé secrète générée aléatoirement, puis à déchiffrer le texte chiffré avec la même clé.

Libsodium contient une API de chiffrement symétrique par défaut appelée "secretbox", qui utilise le système de chiffrement XChaCha20-Poly1305. Il s'agit d'une variante des chiffrements de flux Chacha et Salsa20.

<?php
// Le message en clair
$message = "Hello World";

// On génère la clé secrète avec le Générateur Pseudo-Aléatoire Cryptographique de PHP
// SODIUM_CRYPTO_SECRETBOX_KEYBYTES est une constante de Libsodium qui indique la taille de clé de secretbox, qui est de 32 octets soit 256 bits
$cle_secrete = random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES);

// On peut afficher la clé en la représentant en hexadécimal
echo bin2hex($cle_secrete) . PHP_EOL;

// Le nonce est généré aléatoirement, de taille 192 bits
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);

// On chiffre le message avec la fonction sodium_crypto_secretbox
$texte_chiffre = sodium_crypto_secretbox($message, $nonce, $cle_secrete);

// On affiche le texte chiffré sous forme hexadecimal
echo bin2hex($texte_chiffre) . PHP_EOL;

// On dechiffre le texte chiffré avec la fonction sodium_crypto_secretbox_open
// Notez qu'il est nécessaire de connaitre le nonce pour déchiffrer le texte. Le nonce peut être transmit en clair en même temps que le texte chiffré
$message_dechiffre = sodium_crypto_secretbox_open($texte_chiffre, $nonce, $cle_secrete);

if ($message_dechiffre === false) { 
// Le texte chiffré, le code MAC, la clé ou le nonce est invalide
throw new Exception("Erreur de déchiffrement"); 
}

// On affiche le message déchiffré
echo $message_dechiffre . PHP_EOL;
?>

Stockez un mot de passe de manière sécurisée

Pour stocker un mot de passe de manière sécurisée, il faut utiliser une fonction de hachage de mot de passe, aussi appelée Password-based Key Derivation Function. Libsodium dispose de l'API "pwhash" qui utilise par défaut la fonction Argon2.

<?php
$mot_de_passe_en_clair = "monMot2Passe";

// Retourne le haché du mot de passe avec la fonction Argon2. Le haché contient un salt généré aléatoirement et peut être stocké en base de données
$mot_de_passe_hache = sodium_crypto_pwhash_str(
    $mot_de_passe_en_clair,
    SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE,
    SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE
);

//...
// Vérification du mot de passe
$mot_de_passe_fourni = "monMot2Passe";

// sodium_crypto_pwhash_str_verify s'occupe à la fois de hacher le mot de passe fourni avec le salt du mot de passe stocké et de comparer les 2 hachés. La comparaison se fait en temps constant pour éviter les 'timing attacks", qui permettent de deviner le mot de passe lettre par lettre.
if (sodium_crypto_pwhash_str_verify($mot_de_passe_hache, $mot_de_passe_fourni)) {    
    echo "Mot de passe valide";
} else {    
   echo "Mot de passe invalide";
}
    // Par précaution, on peut effacer de manière sécurisée de la mémoire vive le contenu du mot de passe en clair
    sodium_memzero($mot_de_passe_fourni);
?>

Créez une signature à clé publique

Les signatures électroniques sont très utiles pour s'assurer de l'origine d'un message. Libsodium dispose d'une API "sign" pour signer un message avec une paire de clés privée/publique utilisant EdDSA, une variante de DSA basée sur les courbes elliptiques.

<?php
// Génère une paire de clés privée/publique
$paire_cle_Alice = sodium_crypto_sign_keypair();

    // Extrait chaque clé dans une variable
    $cle_privee_Alice = sodium_crypto_sign_secretkey($paire_cle_Alice);
    $cle_publique_Alice = sodium_crypto_sign_publickey($paire_cle_Alice);

    $message = "Ce message n'est pas confidentiel, mais il est signe par Alice";

$message_signe = sodium_crypto_sign($message, $cle_privee_Alice);
// $message_signe contient à la fois le message et la signature du message.
// Il peut donc être envoyé tel quel

//....
// Pour vérifier la signature
$message_originel = sodium_crypto_sign_open(
    $message_signe,
    $cle_publique_Alice
);
if ($message_originel === false) {
    throw new Exception("Signature invalide");
} else {
    echo $message_originel;
}
?>

Vous pouvez trouver de nombreux autres exemples de Libsodium en PHP dans le livre en ligne, comme par exemple par ici (en anglais).

Utilisez Libsodium en C

PHP fait partie des langages les plus populaires pour développer rapidement et simplement des applications. Cependant, il n'est pas toujours adapté à la cryptographie par rapport à des langages compilés. Le C a de meilleures performances, ce qui peut être important en cryptographie en raison des calculs complexes. Mais surtout, le C permet (ou plutôt oblige !) de gérer manuellement la mémoire. En cryptographie, c'est un avantage car cela permet d'être sûr que le programme se comporte comme défini dans le code. Bien sûr, c'est aussi un gros risque de vulnérabilité, de type dépassement de tampon !

Sous Linux

Vous avez certainement déjà GCC d'installé. Sinon, vous pouvez l'installer avec :

sudo apt install gcc

ou

sudo apt install build-essential

Vous aurez également besoin de la bibliothèque Libsodium pour compiler votre programme. Téléchargez LATEST.tar.gz sur https://download.libsodium.org/libsodium/releases/.

Après avoir extrait l'archive, dans le dossier principal de l'archive entrez les commandes :

./configure
make && make check
sudo make install

Sous Windows

Vous pouvez installer la suite MinGW.

Vous aurez également besoin de la bibliothèque Libsodium pour compiler votre programme.

  1. Téléchargez libsodium-1.0.17-mingw.tar.gz (ou une version correspondante plus récente) sur https://download.libsodium.org/libsodium/releases/ et décompressez l'archive.

  2. Copiez le contenu du dossier "Include" dans le dossier contenant votre fichier de code source C.

  3. Copiez également le fichier bin/libsodium-xx.dll dans le dossier de votre code source C, et renommez-le "libsodium.dll".

Pour compiler et exécuter le programme en ligne de commande, ouvrez un terminal dans le dossier contenant votre code source C (ici le fichier est crypto.c) et entrez :

g++ crypto.c -o crypto -L. -llibsodium -fpermissive -w && crypto

Nous allons créer une application en ligne de commande interactive, qui permet de chiffrer et déchiffrer des messages à l'aide d'une clé secrète.
Tout d'abord, voici le squelette de l'application. Cela permet de gérer le menu et de lire des données fournies par l'utilisateur.

#include <stdio.h>
#include <string.h>
#include "sodium.h"

/* Affiche des données binaires en string hexadécimale 
(terminée par un charactère de fin de chaine) */
void binaire_vers_hexa(unsigned char donnees_binaires[], char donnees_hexa[], int taille_binaire) {
	for (int i = 0; i < taille_binaire; i++) {
		sprintf(&donnees_hexa[i*2], "%02X", donnees_binaires[i]);
	}
}
/* Convertit des données hexadécimales en binaire */
void hexa_vers_binaire(char donnees_hexa[],
		unsigned char donnees_binaires[], int taille_binaire) {
	char octet_hexa_temporaire[3];
	for (int i = 0; i < taille_binaire; i++) {
		memcpy(octet_hexa_temporaire, &donnees_hexa[i*2], 2);
		octet_hexa_temporaire[2] = '\0';
		donnees_binaires[i] = (int) strtol(octet_hexa_temporaire, NULL, 16);
	}
}

/* lit un message entré par l'utilisateur */
int lire(char chaine[], int longueur) {
	char *positionEntree = NULL;
	int temp = 0;
	if (fgets(chaine, longueur, stdin) != NULL) {
		positionEntree = strchr(chaine, '\n');
		if (positionEntree != NULL) {
			*positionEntree = '\0';
		} else {
			while (temp != '\n' && temp != EOF) {
				temp = getchar();
			}

		}
		return 1;
	} else {
		while (temp != '\n' && temp != EOF) {
			temp = getchar();
		}

		return 0;
	}
}

int main() {

	/* Initialisation de libsodium */

	// Menu en ligne de commande
	char choix[2];
	int int_choice;

	do {
		int_choice = -1;
		puts("");
		puts("Chiffrement symetrique -- Menu: \n");
		puts("1. Chiffrer un message");
		puts("2. Dechiffrer un message");
		puts("3. Chiffrer un message avec AES-GCM");
		puts("4. Dechiffrer un message avec AES-GCM");
		puts("0. Quitter le programme");
		lire(choix, 2);

		sscanf(choix, "%d", &int_choice);
		switch (int_choice) {
		case 1:
			/* chiffrer(cle_secrete); */
			break;
		case 2:
			/* dechiffrer(cle_secrete); */
			break;
		case 3:
			/* chiffrer_AES_GCM(cle_secrete, nonce_AES_GCM); */
			break;
		case 4:
			/* dechiffrer_AES_GCM(cle_secrete); */
			break;
		case 0:
			break;
		default:
			printf("Erreur - commande inconnue %s", choix);
			break;
		}
	} while (int_choice != 0);

	return EXIT_SUCCESS;
}

Vous pouvez compiler l'application et la lancer pour tester avec la commande :

g++ crypto.c -o crypto -L. -llibsodium -fpermissive -w && crypto

Nous allons maintenant initialiser la librairie Libsodium. Ajoutez le code suivant sous /* Initialisation de libsodium */  :

	/* Initialisation de libsodium */
	if (sodium_init() < 0) {
		puts("Erreur - La librairie libsodium n'a pas pu s'initialiser");
		return EXIT_FAILURE;
	    }
	    
	unsigned char cle_secrete[crypto_secretbox_KEYBYTES]; //32 octets
	// GNPA cryptographique de Libsodium
	randombytes_buf(cle_secrete, crypto_secretbox_KEYBYTES);
  • la fonction sodium_init() n'est pas obligatoire, mais elle améliore les performances, et surtout fait des vérifications de sécurité, comme vérifier que le système est capable de générer de l'entropie de manière sécurisée ;

  • la variable cle_secrete contiendra la clé secrète. Il s'agit d'un tableau (array) de type unsigned char, c'est-à-dire d'octets bruts ; en d'autres termes, d'une suite de bits. Contrairement à une chaîne de caractères, il n'y a pas de formatage ni de caractère de fin de chaîne ;

  • crypto_secretbox_KEYBYTES est une constante qui définit la taille de la clé pour l'API SecretBox, en octets. Sa valeur est de 32, ce qui correspond à une clé de 32 x 8 = 256 bits ;

  • la fonction randombytes_buf() est le générateur de nombres pseudo-aléatoires cryptographiques de Libsodium. En effet, le C ne possède pas de GNPA cryptographique par défaut. En interne, cette fonction utilise le GNPA du système d'exploitation.

Nous allons maintenant ajouter la fonction chiffrer(). Décommentez la fonction chiffrer(cle_secrete); au niveau du Case 1 du main. Puis ajoutez le code suivant au-dessus de la fonction main :

void chiffrer(unsigned char cle_secrete[]) {

// lecture du message
	char message_en_clair[1001];
	puts("Entrez le message en clair (sans retour a la ligne, au maximum 1 000 caracteres)");
	lire(message_en_clair, 1001);

// calcul de la taille du message
	unsigned int taille_clair = strlen(message_en_clair); // notez que le caractère de fin de chaine n'est pas compté et ne sera pas inclu dans le texte chiffré
// calcul de la taille du texte chiffré
	unsigned char texte_chiffre[taille_clair + crypto_secretbox_MACBYTES]; // le texte chiffré fait la taille du message en clair + le code MAC de 16 octets 

// génération d'un nonce aléatoire
	unsigned char nonce[crypto_secretbox_NONCEBYTES]; // nonce de 24 octets soit 192 bits
	randombytes_buf(nonce, sizeof nonce); // Nonce généré aléatoirement, sa taille est suffisemment grande pour qu'il ne se répète jamais deux fois

// représentation du nonce en chaine hexadecimale (pour l'afficher ou le transmettre facilement)
	char nonce_hex[crypto_secretbox_NONCEBYTES * 2 + 1];  //(2 fois la taille de la représentation binaire plus 1 caractère de fin de chaine)
	binaire_vers_hexa(nonce, nonce_hex, sizeof nonce);


	// Chiffrement du message avec la clé secrete et le nonce
	crypto_secretbox_easy(texte_chiffre, message_en_clair,
			taille_clair, nonce, cle_secrete);

 // représentation du texte chiffré en string hexadecimale
	char texte_chiffre_hex[sizeof texte_chiffre * 2 + 1]; // (2 fois la taille en binaire plus 1 charactère NULL de fin de string)
	binaire_vers_hexa(texte_chiffre, texte_chiffre_hex, sizeof texte_chiffre);

// affichage du couple nonce + texte chiffré
	puts("NONCE : TEXTE CHIFFRE (a copier)");
	printf("%s:%s\n", nonce_hex, texte_chiffre_hex);
}

Prenez le temps de comprendre le code et les étapes. Le chiffrement en lui-même est assez simple et similaire à PHP. Le plus compliqué est de gérer la taille mémoire des variables et de convertir les données binaires en chaînes hexadécimales pour les afficher ou les transmettre facilement.

Pour déchiffrer, décommentez le code "dechiffrer(cle_secrete);" dans le Case 2 du main, puis copiez le code suivant sous la fonction chiffrer() :

void dechiffrer(unsigned char cle_secrete[]) {

// lecture du texte chiffré
	char couple_nonce_chiffre[1000 + crypto_secretbox_NONCEBYTES + 1 + crypto_secretbox_MACBYTES]; // taille max du couple nonce + chiffre + separateur
	printf("Entrez un couple NONCE : TEXTE CHIFFRE (coller)\n");
	lire((char*) couple_nonce_chiffre, 1000);

// On extrait le nonce et le convertit en binaire
	unsigned char nonce_bin[crypto_secretbox_NONCEBYTES];
	hexa_vers_binaire(couple_nonce_chiffre, nonce_bin, crypto_secretbox_NONCEBYTES); 

// On extrait le texte chiffré et le convertit en binaire
	int indice_debut_chiffre = crypto_secretbox_NONCEBYTES * 2 + 1; // indice du premier caractere du chiffré dans le couple nonce chiffré)
	int taille_chiffre = strlen(couple_nonce_chiffre) - indice_debut_chiffre; // On calcule la taille du chiffre (taille du couple moins le nonce et le séparateur ":")

	unsigned char texte_chiffre_bin[taille_chiffre / 2]; // La taille du chiffré binaire est la moitié de la taille du chiffré hexa
	hexa_vers_binaire(&couple_nonce_chiffre[indice_debut_chiffre], texte_chiffre_bin, sizeof texte_chiffre_bin);

// On calcule la taille du texte en clair
	char message_en_clair[(sizeof texte_chiffre_bin - crypto_secretbox_MACBYTES) + 1]; // le message en clair fait la taille du chiffre binaire moins le code MAC, plus 1 charactere NULL final

	
// On déchiffre le texte chiffré avec le nonce et la clé secrète
if (crypto_secretbox_open_easy(message_en_clair, texte_chiffre_bin,
			sizeof texte_chiffre_bin, nonce_bin, cle_secrete) != 0) {
		puts(
				"Erreur lors du déchiffrement, la cle secrete, le nonce ou le texte chiffre n'est pas correct");

	}
	message_en_clair[sizeof message_en_clair - 1] = '\0'; // On ajoute le charactère NULL final

// On affiche le message déchiffré
	puts("Message dechiffre:");
	puts(message_en_clair);
}

Comme pour le chiffrement, le plus compliqué est de calculer les tailles des variables et de convertir d'hexadécimal vers le binaire.

Vous pouvez maintenant compiler et tester l'application. Pour l'utiliser, il faut entrer 1 puis entrée dans le menu. Entrez un message et faites entrée, puis copiez le couple nonce:chiffré qui s'affiche (sélectionner puis clic droit). Faites ensuite 2 puis entrée dans le menu et collez les données (clic droit), puis faites entrée. Si tout se passe bien, le message déchiffré s'affiche.

Chiffrez avec AES-GCM

AES-GCM est un des modes de chiffrement authentifié les plus populaires, notamment en raison de sa rapidité lorsqu'il est accéléré matériellement. La principale différence avec XChaCha20-Poly1305, est que le nonce est plus petit (96 bits). Il est donc déconseillé de générer un nonce aléatoire, à cause du risque d'utiliser 2 fois le même nonce avec la même clé, ce qui détruirait la confidentialité des messages. Il faut donc utiliser un compteur et vérifier si l'on a atteint la limite du compteur, c'est-à-dire si l'on a chiffré 2^96 messages avec la même clé.

Dans le main, nous allons rajouter une variable nonce_AES_GCM en dessous de la génération de la clé secrète. Ce compteur est initialisé avec des 0.

// GNPA cryptographique de Libsodium
randombytes_buf(cle_secrete, crypto_secretbox_KEYBYTES);

unsigned char nonce_AES_GCM[crypto_aead_aes256gcm_NPUBBYTES] = {0}; // 12 octets, initialisé avec tous les bits à 0

Décommentez ensuite les fonctions chiffrer_AES_GCM(cle_secrete, nonce_AES_GCM); et déchiffrer_AES_GCM(cle_secrete); dans les cases 3 et 4 du main.

Copiez les fonctions suivantes sous la fonction déchiffrer :

void chiffrer_AES_GCM(unsigned char cle_secrete[], unsigned char nonce[]) {

	char message_en_clair[1001];
	puts("Entrez le message en clair (sans retour a la ligne, au maximum 1 000 caracteres)");
		lire(message_en_clair, 1001);

		unsigned int taille_clair = strlen(message_en_clair);
		// le texte chiffré inclue un code MAC de 16 octets soit 128 bits
		unsigned char texte_chiffre[taille_clair + crypto_aead_aes256gcm_ABYTES]; // le texte chiffré fait la taille du message en clair plus le code MAC


		unsigned char nonce_initial[crypto_aead_aes256gcm_NPUBBYTES] = {0};

		sodium_increment(nonce, crypto_aead_aes256gcm_NPUBBYTES); // On utilise le nonce en mode compteur

		if(0 == sodium_compare(nonce, nonce_initial, crypto_aead_aes256gcm_NPUBBYTES)){ // On verifie si le nonce est revenue à la valeur initiale
			puts("Vous avez épuisez tous les nonces possibles avec la même clé.");
			return;
		}
		char nonce_hex[crypto_aead_aes256gcm_NPUBBYTES * 2 + 1];
		binaire_vers_hexa(nonce, nonce_hex, crypto_aead_aes256gcm_NPUBBYTES);

		/* Encrypt MESSAGE using key and nonce
		 Encrypted message is stored in ciphertext buffer */
		crypto_aead_aes256gcm_encrypt(texte_chiffre, NULL,
				message_en_clair, taille_clair,
		                              NULL, NULL,
		                              NULL, nonce, cle_secrete);

		char texte_chiffre_hex[sizeof texte_chiffre * 2 + 1]; // représentation du texte chiffré en string hexadecimale (2 fois la taille en binaire plus 1 charatère NULL de fin de string)
		binaire_vers_hexa(texte_chiffre, texte_chiffre_hex, sizeof texte_chiffre);

		puts("NONCE : TEXTE CHIFFRE (à copier)");
		printf("%s:%s\n", nonce_hex, texte_chiffre_hex);
}

void dechiffrer_AES_GCM(unsigned char cle_secrete[]) {

	char couple_nonce_chiffre[1000 + crypto_aead_aes256gcm_NPUBBYTES + 1 + crypto_aead_aes256gcm_ABYTES]; // taille max du couple nonce + chiffre + separateur
	puts("Entrez un couple NONCE : TEXTE CHIFFRE (coller)");
	lire(couple_nonce_chiffre, 1000);

	unsigned char nonce_bin[crypto_aead_aes256gcm_NPUBBYTES];
	hexa_vers_binaire(couple_nonce_chiffre, nonce_bin, crypto_aead_aes256gcm_NPUBBYTES); // On extrait et convertit le nonce en binaire

	int indice_debut_chiffre = crypto_aead_aes256gcm_NPUBBYTES * 2 + 1; // indice du premier charactere du chiffré dans le couple nonce chiffré)
	int taille_chiffre = strlen(couple_nonce_chiffre) - indice_debut_chiffre; // On calcule la taille du chiffre (taille du couple moins le nonce et le séparateur ":")

	unsigned char texte_chiffre_bin[taille_chiffre / 2]; // La taille du chiffré binaire est la moitié de la taille du chiffré hexa
	hexa_vers_binaire(&couple_nonce_chiffre[indice_debut_chiffre], texte_chiffre_bin, sizeof texte_chiffre_bin);

	char message_en_clair[(sizeof texte_chiffre_bin - crypto_aead_aes256gcm_ABYTES) + 1]; // le message en clair fait la taille du chiffre binaire moins le code MAC, plus 1 charactere NULL final

	if (crypto_aead_aes256gcm_decrypt(message_en_clair, NULL, NULL, texte_chiffre_bin, sizeof texte_chiffre_bin, NULL, NULL, nonce_bin, cle_secrete) != 0) {
		puts("Erreur lors du déchiffrement, la cle secrete, le nonce ou le texte chiffre n'est pas correct");

	}
	message_en_clair[sizeof message_en_clair - 1] = '\0'; // On ajoute le charactère NULL final
	puts("Message dechiffre:");
	puts(message_en_clair);

}

Ces fonctions sont très similaires aux précédentes utilisant XChaCha20. Les seules différences sont la taille du nonce et du code MAC, et la génération du nonce avant le chiffrement. Le nonce fonctionne comme un compteur, donc comme un nombre. Cependant il est bien trop grand pour être contenu dans un int (max 2^15-1) ou même dans un unsigned long long  (max 2^64-1). Libsodium fournit des fonctions pour incrémenter (sodium_increment) et pour comparer (sodium_compare) des nonces ou toute donnée binaire de taille arbitraire. Ce problème de taille des nombres est courant en cryptographie, c'est pourquoi on utilise souvent des types de nombres de taille arbitraire appelés Big Number ou BigInteger.

Dérivez plusieurs clés à partir d'une Master Key

Nous allons faire une dernière modification dans notre application. Nous utilisons la même clé secrète pour le chiffrement avec XChaCha20 et avec AES-GCM. C'est une très mauvaise pratique ! Une clé secrète ne doit être utilisée que pour une seule fonction, par sécurité. On pourrait générer une autre clé aléatoire pour le chiffrement AES-GCM, mais dans ce cas il faudrait échanger plusieurs clés avec ses interlocuteurs, ce qui n'est pas toujours pratique.

Une autre solution est d'utiliser un GNPA déterministe pour générer plusieurs clés à partir d'une seule clé. On appelle cette fonction une KDF, ou Key Derivation Function. Vous avez déjà vu les password-based KDF dans la partie dédiée au stockage de mot de passe. les password-based KDF sont une catégorie particulière de KDF, qui sont très lentes. Lorsque l'on utilise une clé aléatoire en entrée, on peut utiliser des KDF beaucoup plus rapides.

Libsodium dispose d'une fonction pour dériver plusieurs clés d'une clé principale qu'on appelle Master Key. Ainsi, on n'a besoin d'échanger qu'une seule clé secrète, et on peut ensuite dériver des clés différentes pour chaque fonction cryptographique. Comme la KDF est un GNPA cryptographique, une clé secrète dérivée ne donne aucune information sur la Master Key, ni sur les autres clés secrètes dérivées.

Dans le main, remplacez

unsigned char cle_secrete[crypto_secretbox_KEYBYTES]; //32 octets

// GNPA cryptographique de Libsodium
randombytes_buf(cle_secrete, crypto_secretbox_KEYBYTES);

par

unsigned char master_key[crypto_kdf_KEYBYTES]; //32 octets
unsigned char cle_secrete_secretbox[crypto_secretbox_KEYBYTES];
unsigned char cle_secrete_AES_GCM[crypto_aead_aes256gcm_KEYBYTES];

randombytes_buf(master_key, crypto_kdf_KEYBYTES);

crypto_kdf_derive_from_key(cle_secrete_secretbox, sizeof cle_secrete_secretbox, 1, "CONTEXT_", master_key);
crypto_kdf_derive_from_key(cle_secrete_AES_GCM, sizeof cle_secrete_AES_GCM, 2, "CONTEXT_", master_key);

Remplacez dans le switch/case les cases suivants :

		switch (int_choice) {
		case 1:
			chiffrer(cle_secrete_secretbox);
			break;
		case 2:
			dechiffrer(cle_secrete_secretbox);
			break;
		case 3:
			chiffrer_AES_GCM(cle_secrete_AES_GCM, nonce_AES_GCM);
			break;
		case 4:
			dechiffrer_AES_GCM(cle_secrete_AES_GCM);

Votre application est maintenant prête à être utilisée. Bien sûr, ce n'est qu'une application de démonstration, qui ne doit pas être utilisée directement en production ! Il faudrait au minimum ajouter des vérifications sur les entrées utilisateurs, une gestion des erreurs, etc.
Pour aller plus loin, vous pouvez consulter la documentation de Libsodium.

En résumé

  • La librairie Libsodium fournit des API simples et sécurisées pour la plupart des opérations cryptographiques, et est disponible dans la plupart des langages de développement ; 

  • les algorithmes de cryptographie sont généralement codés en C afin d'avoir une gestion précise de la mémoire et du comportement de l'application ; 

  • pour chiffrer de manière sécurisée, on utilise de préférence un système de chiffrement authentifié afin de garantir l'intégrité des données ; 

  • les erreurs d'implémentation d'algorithmes cryptographiques sont courantes, et détruisent généralement la sécurité des applications. Il est donc conseillé de toujours utiliser des bibliothèques cryptographiques reconnues, des systèmes cryptographiques standard, de suivre les bonnes pratiques de développement et de faire auditer les applications par des cryptologues et cryptanalystes spécialisés.

Vous êtes dorénavant arrivé à la fin de ce cours ! J'espère que vous avez découvert toutes les clés de la cryptographie. Il ne vous reste qu'à effectuer l'activité pour valider le cours et obtenir votre certificat. Allez, encore un petit effort ! :)

Exemple de certificat de réussite
Exemple de certificat de réussite