Fonctions de hachage et modes opératoires
Ce chapitre approfondit l'étude des fonctions de hachage cryptographiques et des différents modes opératoires utilisés en cryptographie symétrique. Ces deux éléments sont fondamentaux pour la sécurité des systèmes cryptographiques modernes et sont utilisés dans de nombreuses applications sécurisées.
2.1. Fonctions de hachage cryptographiques avancées
Structure interne des fonctions de hachage modernes
Les fonctions de hachage cryptographiques modernes sont généralement construites selon l'une des trois approches suivantes :
Construction Merkle-Damgård
Utilise une fonction de compression itérative avec chaînage pour transformer un message de taille arbitraire en une empreinte de taille fixe.
Exemples : MD5, SHA-1, SHA-2
Construction Sponge
Utilise une fonction de permutation avec des phases d'absorption et de pressage, permettant une sortie de taille variable.
Exemples : SHA-3 (Keccak), SHAKE
HAIFA
HAsh Iterative FrAmework, une extension de Merkle-Damgård incluant un compteur de bits et du salage.
Exemples : BLAKE2, BLAKE3
Analyse approfondie des principales fonctions de hachage
Fonction | Année | Construction | Taille de sortie | Propriétés notables | État de sécurité |
---|---|---|---|---|---|
MD5 | 1992 | Merkle-Damgård | 128 bits | Rapide, sortie compacte | Cassé Collisions trouvées en 2004 |
SHA-1 | 1995 | Merkle-Damgård | 160 bits | Anciennement très utilisé (Git, TLS) | Cassé Collision pratique démontrée en 2017 |
SHA-2 (SHA-256/512) | 2001 | Merkle-Damgård | 256/512 bits | Famille de fonctions, résistant aux attaques length-extension | Sûr Standard actuel |
SHA-3 (Keccak) | 2015 | Sponge | 224/256/384/512 bits | Construction fondamentalement différente de SHA-2 | Sûr Standard NIST actuel |
BLAKE2 | 2012 | HAIFA | Variable (jusqu'à 512 bits) | Optimisé pour la vitesse, paramétrisation flexible | Sûr Alternative rapide à SHA-3 |
BLAKE3 | 2020 | Merkle + HAIFA | Variable (défaut 256 bits) | Parallélisable, extrêmement rapide | Sûr État de l'art |
Fonctionnement interne de SHA-256
SHA-256 est l'une des fonctions de hachage les plus utilisées aujourd'hui. Examinons son fonctionnement interne en détail :
- Préparation des messages
- Padding : ajout d'un bit '1' suivi de '0's pour atteindre une longueur de 448 modulo 512 bits
- Ajout de la longueur originale du message sur 64 bits
- Division en blocs de 512 bits (16 mots de 32 bits chacun)
- Initialisation des valeurs de hachage
- Huit constantes initiales (dérivées des parties fractionnaires des racines carrées des 8 premiers nombres premiers) :
h[0] = 0x6a09e667 h[4] = 0x510e527f h[1] = 0xbb67ae85 h[5] = 0x9b05688c h[2] = 0x3c6ef372 h[6] = 0x1f83d9ab h[3] = 0xa54ff53a h[7] = 0x5be0cd19
- Huit constantes initiales (dérivées des parties fractionnaires des racines carrées des 8 premiers nombres premiers) :
- Préparation de la table de constantes
- 64 constantes dérivées des parties fractionnaires des racines cubiques des 64 premiers nombres premiers
- Traitement des blocs
- Extension du bloc en 64 mots de 32 bits
- Application de la fonction de compression principale
- Mise à jour des valeurs de hachage intermédiaires
- Production du digest final
- Concaténation des huit valeurs de hachage finales h[0]...h[7]
Au cœur de SHA-256 se trouvent les fonctions suivantes :
$$Ch(x, y, z) = (x \land y) \oplus (\neg x \land z)$$
$$Maj(x, y, z) = (x \land y) \oplus (x \land z) \oplus (y \land z)$$
$$\Sigma_0(x) = ROTR^2(x) \oplus ROTR^{13}(x) \oplus ROTR^{22}(x)$$
$$\Sigma_1(x) = ROTR^6(x) \oplus ROTR^{11}(x) \oplus ROTR^{25}(x)$$
où \(ROTR^n(x)\) représente la rotation à droite de \(x\) de \(n\) bits.
// Initialiser les registres de travail a = h[0]; b = h[1]; c = h[2]; d = h[3]; e = h[4]; f = h[5]; g = h[6]; h = h[7]; // Boucle principale de compression for (t = 0; t < 64; t++) { T1 = h + Σ₁(e) + Ch(e, f, g) + K[t] + W[t]; T2 = Σ₀(a) + Maj(a, b, c); h = g; g = f; f = e; e = d + T1; d = c; c = b; b = a; a = T1 + T2; } // Mettre à jour les valeurs de hachage h[0] += a; h[1] += b; h[2] += c; h[3] += d; h[4] += e; h[5] += f; h[6] += g; h[7] += h;
Construction Sponge et SHA-3
SHA-3 (Keccak) utilise une construction fondamentalement différente appelée "construction éponge" :
- État interne : Matrice de 5×5 mots de 64 bits (1600 bits au total)
- Phase d'absorption : Le message est absorbé bloc par bloc en faisant un XOR avec une partie de l'état (la "rate")
- Phase de pressurage : L'état est "pressé" pour produire le hash final
- Avantages :
- Résistance naturelle aux attaques length-extension
- Sortie de taille arbitraire (XOFs - fonctions de sortie extensible)
- Construction modulaire et flexible
Exemple : Calcul SHA-256 en pseudo-code Python
import hashlib message = "Hello, World!" # Encode en UTF-8 et calcule le hash SHA-256 sha256_hash = hashlib.sha256(message.encode('utf-8')).hexdigest() print(f"SHA-256 de '{message}' : {sha256_hash}") # Implémentation manuelle des principales étapes (simplifié) def manual_sha256(message): # Initialisation des valeurs de hachage (h0 à h7) h = [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19] # Préparation du message (padding) # ... code de préparation ... # Traitement pour chaque bloc de 512 bits for block in blocks: # Expansion du bloc en 64 mots de 32 bits w = expand_block(block) # Initialisation des registres de travail a, b, c, d, e, f, g, h_temp = h # Boucle principale de compression for i in range(64): # ... fonctions de compression ... # Mise à jour des valeurs de hachage h[0] += a h[1] += b # ... et ainsi de suite # Assemblage du digest final return ''.join(format(x, '08x') for x in h)
Résultat : dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f
Applications avancées des fonctions de hachage
Utilisé pour l'authentification de messages à l'aide d'une fonction de hachage cryptographique et d'une clé secrète.
$$HMAC(K, m) = H((K' \oplus opad) \| H((K' \oplus ipad) \| m))$$
où :
- \(H\) est la fonction de hachage
- \(K'\) est la clé dérivée de \(K\)
- \(opad\) et \(ipad\) sont des constantes
- \(\|\) représente la concaténation
Applications : TLS, signatures d'API, jetons d'authentification (JWT)
Méthode standardisée pour dériver des clés cryptographiques à partir d'une entrée à faible entropie ou d'un secret partagé.
Comporte deux étapes principales :
- Extraction : crée une clé pseudoaléatoire (PRK) à partir du matériel source
- Expansion : génère des clés de sortie déterministes de longueur arbitraire
$$PRK = HMAC(salt, IKM)$$
$$OKM = HMAC(PRK, T(1) \| info) \| HMAC(PRK, T(2) \| info) \| ...$$
Applications : Dérivation de clés dans TLS 1.3, Signal Protocol
Arbre de Merkle
Structure de données arborescente où chaque feuille contient le hash d'un bloc de données, et chaque nœud interne contient le hash de ses enfants.
Applications : Blockchain, Git, IPFS, certificats transparents
Fonctions de hachage utilisées pour prouver qu'un certain travail computationnel a été effectué. Le défi consiste généralement à trouver une entrée produisant un hash avec un certain nombre de zéros au début.
// Exemple simplifié de preuve de travail // (similaire à Bitcoin) function mineBlock(data, difficulty) { let nonce = 0; let hash; // Cible : hash commençant par 'difficulty' zéros const target = "0".repeat(difficulty); while(true) { // Concaténer les données avec le nonce const attempt = data + nonce.toString(); // Calculer le hash hash = sha256(attempt); // Vérifier si le hash commence par le nombre // requis de zéros if(hash.substring(0, difficulty) === target) { return { nonce, hash }; } nonce++; } }
Applications : Cryptocurrencies, protection contre le spam, mécanismes anti-DoS
2.2. Modes opératoires de chiffrement
Principes fondamentaux des modes opératoires
Les modes opératoires sont des méthodes pour appliquer un algorithme de chiffrement par bloc (comme AES) à des messages de longueur arbitraire. Ils déterminent comment les blocs sont traités et combinés, ce qui affecte directement les propriétés de sécurité du système cryptographique.
Modes opératoires classiques
Chaque bloc est chiffré indépendamment avec la même clé :
$$C_i = E_K(P_i)$$
Avantages : Simple, parallélisable
Inconvénients : Ne cache pas les motifs (les blocs identiques produisent des chiffrés identiques), vulnérable aux attaques par replay
Verdict : Ne jamais utiliser
Chaque bloc de texte clair est combiné (XOR) avec le bloc chiffré précédent avant d'être chiffré :
$$C_i = E_K(P_i \oplus C_{i-1})$$
$$C_0 = IV$$ (vecteur d'initialisation)
Avantages : Cache les motifs, un changement dans un bloc se propage à tous les blocs suivants
Inconvénients : Non parallélisable pour le chiffrement, vulnérable aux attaques par padding oracle
Verdict : Utilisable avec précaution
Transforme un chiffrement par bloc en chiffrement à flot en chiffrant des valeurs de compteur incrémentales :
$$C_i = P_i \oplus E_K(Nonce \| Counter_i)$$
Avantages : Parallélisable (chiffrement et déchiffrement), transforme un chiffrement par bloc en chiffrement à flot, accès aléatoire aux blocs
Inconvénients : Vulnérable si le nonce est réutilisé, pas d'authentification intégrée
Verdict : Recommandé avec nonces uniques
Extension du mode CTR avec une fonction d'authentification basée sur la multiplication en champ de Galois :
- Utilise CTR pour le chiffrement
- Calcule un tag d'authentification (GMAC) pour garantir l'intégrité
- Permet d'authentifier des données additionnelles (AAD) non chiffrées
$$C_i = P_i \oplus E_K(IV \| Counter_i)$$
$$Tag = GHASH(AAD, C) \oplus E_K(IV \| 0)$$
Avantages : Parallélisable, fournit confidentialité et authentification (AEAD), très efficace sur le matériel moderne
Inconvénients : Catastrophique si le nonce est réutilisé, implémentation complexe
Verdict : Hautement recommandé pour les applications modernes
Modes opératoires avancés et spécialisés
Combinaison du chiffrement à flot ChaCha20 et de l'authentificateur Poly1305 :
- ChaCha20 : fonction de chiffrement à flot ARX (Addition-Rotation-XOR) créée par D.J. Bernstein
- Poly1305 : fonction d'authentification MAC basée sur un polynôme
$$C = P \oplus ChaCha20(key, nonce, counter)$$
$$Tag = Poly1305(r, C)$$
où \(r\) est une clé dérivée de la clé principale et du nonce
Avantages : Très rapide sur les processeurs sans instructions AES-NI, résistant aux attaques par canal auxiliaire, sans tableau de substitution
Verdict : Recommandé particulièrement pour les appareils à ressources limitées
Mode de chiffrement authentifié conçu pour être résistant à la réutilisation de nonce :
- Calcule d'abord un "IV synthétique" (S) basé sur les données à chiffrer
- Utilise ensuite S comme nonce pour chiffrer les données avec CTR
$$S = CMAC_K(AAD \| P)$$
$$C = CTR_{K'}(S, P)$$
Avantages : Résistant à la réutilisation accidentelle de nonce/IV, idéal pour le chiffrement de stockage
Inconvénients : Nécessite deux passes sur les données, pas parallélisable pour le chiffrement
Verdict : Recommandé pour le chiffrement au repos et les environnements où la gestion de nonce est difficile
Mode spécialisé pour le chiffrement de stockage (disques, secteurs) :
- Utilise deux clés et un "tweak" (généralement le numéro de secteur)
- Chaque bloc est chiffré indépendamment mais avec une modification basée sur sa position
$$C_j = E_{K1}(P_j \oplus (E_{K2}(i) \otimes \alpha^j)) \oplus (E_{K2}(i) \otimes \alpha^j)$$
où \(i\) est l'identifiant du secteur et \(\alpha\) est un élément primitif du champ de Galois
Avantages : Accès aléatoire aux blocs, pas de propagation d'erreurs, adapté au chiffrement de disque
Inconvénients : Pas d'authentification intégrée, vulnérable à certaines attaques de modification
Verdict : Recommandé spécifiquement pour le chiffrement de stockage
Mode de chiffrement authentifié parallélisable en une seule passe :
- Utilise des "offsets" calculés à partir du nonce pour chaque bloc
- Fournit l'authentification et le chiffrement en une seule passe
$$C_i = E_K(P_i \oplus \Delta_i) \oplus \Delta_i$$
$$Tag = E_K(\bigoplus_i (P_i \oplus C_i) \oplus \Delta_*)$$
Avantages : Très efficace (presque aussi rapide que le chiffrement CTR seul), entièrement parallélisable
Inconvénients : Protégé par des brevets jusqu'en 2021 (maintenant libre), légèrement plus vulnérable à la réutilisation de nonce que GCM
Verdict : Recommandé pour les applications nécessitant des performances optimales
Sécurité et vulnérabilités des modes opératoires
Vulnérabilité | Description | Modes affectés | Contre-mesures |
---|---|---|---|
Révélation de motifs | Blocs identiques produisant des chiffrés identiques | ECB | Utiliser des modes avec chaînage ou compteur (CBC, CTR) |
Attaques par padding oracle | Exploite les informations de validation du padding pour récupérer progressivement le texte clair | CBC avec padding PKCS#7 | Utiliser des modes AEAD, valider l'intégrité avant le padding |
Réutilisation de nonce/IV | Compromet gravement la sécurité si un nonce est réutilisé | CTR, GCM, ChaCha20-Poly1305 | Gestion stricte des nonces, utiliser des modes SIV |
Attaques par bits flippés | Modification délibérée de bits dans le chiffré pour produire des changements prévisibles dans le texte déchiffré | Modes sans authentification (ECB, CBC, CTR) | Utiliser des modes AEAD (GCM, ChaCha20-Poly1305) |
Attaques par canal auxiliaire | Exploitation du timing, de la consommation électrique ou des fuites EM pour récupérer des informations | Tous les modes, mais particulièrement ceux avec des opérations à temps variable | Implémentations à temps constant, protections matérielles |
Recommandations pratiques pour le choix d'un mode opératoire
- Communications réseau :
- Privilégier AES-GCM ou ChaCha20-Poly1305
- Utiliser des nonces incrémentaux ou aléatoires de taille suffisante
- S'assurer que le récepteur vérifie le tag d'authentification avant d'utiliser les données déchiffrées
- Chiffrement de fichiers :
- Privilégier AES-GCM pour les petits fichiers
- Considérer AES-SIV pour les fichiers fréquemment mis à jour
- Stocker le tag d'authentification avec les données chiffrées
- Chiffrement de disque/partition :
- Privilégier AES-XTS
- Considérer des solutions avec intégrité comme AEGIS si l'authentification est nécessaire
- Environnements contraints :
- Privilégier ChaCha20-Poly1305 sur les plateformes sans accélération AES
- Considérer ASCON ou d'autres chiffrements légers standardisés pour l'IoT
- Haute performance :
- Privilégier AES-GCM avec accélération matérielle ou OCB
- Utiliser AES-GCM-SIV si la gestion des nonces est problématique
Exemple : Utilisation d'AES-GCM en OpenSSL
# Génération d'une clé AES-256 aléatoire (32 octets) openssl rand -out key.bin 32 # Génération d'un vecteur d'initialisation (IV/nonce) de 12 octets openssl rand -out iv.bin 12 # Chiffrement avec AES-256-GCM openssl enc -aes-256-gcm -in plaintext.txt -out ciphertext.bin \ -K $(xxd -p -c 64 key.bin) \ -iv $(xxd -p -c 24 iv.bin) \ -out ciphertext.bin \ -tag tag.bin # Déchiffrement avec AES-256-GCM openssl enc -d -aes-256-gcm -in ciphertext.bin \ -K $(xxd -p -c 64 key.bin) \ -iv $(xxd -p -c 24 iv.bin) \ -tag tag.bin \ -out decrypted.txt
- Un IV peut être aléatoire et doit simplement être unique avec une haute probabilité
- Un nonce doit être rigoureusement unique (never used more than once) pour le même contexte de chiffrement
Exercices associés
Mettez en pratique les concepts de ce chapitre avec les exercices dédiés.
Accéder aux exercicesRessources complémentaires
- "Cryptography Engineering" - Ferguson, Schneier, Kohno
- NIST SP 800-38D: Recommendation for Block Cipher Modes (GCM)
- The Keccak (SHA-3) Reference
- CMAC Mode (Cipher-based MAC)